ez_token/services/authentication/authenticator.rs
1use crate::cli::output::{print_success, print_warning};
2use arboard::Clipboard;
3use miette::Result;
4
5/// Defines the interface for an OAuth2 authentication flow.
6///
7/// Implementors are responsible for performing the full token exchange
8/// and returning a raw access token string on success.
9///
10/// # Implementors
11///
12/// - [`super::pkce::AuthorizationCodeFlow`] — Interactive browser login via PKCE.
13/// - [`super::client_credentials::ClientCredentialsFlow`] — Machine-to-machine via client secret.
14#[allow(async_fn_in_trait)]
15pub trait Authenticator {
16 /// Performs the authentication flow and returns a raw access token.
17 ///
18 /// # Errors
19 ///
20 /// Returns an error if the token exchange fails for any reason,
21 /// such as invalid credentials, network issues, or a rejected authorization.
22 async fn get_token(&self) -> Result<String>;
23}
24
25/// Executes an authentication flow and handles the resulting token.
26///
27/// Calls [`Authenticator::get_token`] on the provided flow, then attempts
28/// to copy the token to the system clipboard. If clipboard access is
29/// unavailable or fails, the token is printed to standard output as a fallback.
30///
31/// # Arguments
32///
33/// * `flow` - Any type implementing the [`Authenticator`] trait.
34///
35/// # Errors
36///
37/// Returns an error if the underlying authentication flow fails.
38/// Clipboard errors are handled gracefully and do not cause the function to fail.
39pub async fn execute_flow<A: Authenticator>(flow: &A) -> Result<()> {
40 let token = flow.get_token().await?;
41
42 match Clipboard::new() {
43 Ok(mut cb) => {
44 if let Err(e) = cb.set_text(token.clone()) {
45 print_warning(&format!("Failed to copy to clipboard: {}", e));
46 println!("Token: {}", token);
47 } else {
48 print_success("Token copied to clipboard!");
49 }
50 }
51 Err(_) => {
52 print_warning("Could not access clipboard.");
53 println!("Token: {}", token);
54 }
55 }
56
57 Ok(())
58}