use crate::cli::output::{finish_spinner_error, finish_spinner_success, start_spinner};
use crate::services::authentication::authenticator::Authenticator;
use crate::services::authentication::urls::IdentityProvider;
use crate::services::http_client::client::create_http_client;
use miette::{Context, IntoDiagnostic, Result};
use oauth2::{ClientId, ClientSecret, Scope, TokenResponse, TokenUrl, basic::BasicClient};
pub struct ClientCredentialsFlow {
pub provider: IdentityProvider,
pub client_id: String,
pub client_secret: String,
pub scopes: Vec<String>,
}
impl Authenticator for ClientCredentialsFlow {
async fn get_token(&self) -> Result<String> {
let token_uri = TokenUrl::new(self.provider.token_url())
.into_diagnostic()
.wrap_err("Invalid token URL")?;
let client = BasicClient::new(ClientId::new(self.client_id.clone()))
.set_client_secret(ClientSecret::new(self.client_secret.clone()))
.set_token_uri(token_uri);
let mut token_req = client.exchange_client_credentials();
if let Some(audience) = self.provider.audience() {
token_req = token_req.add_extra_param("audience", audience);
}
for scope in &self.scopes {
token_req = token_req.add_scope(Scope::new(scope.clone()));
}
let http_client = create_http_client()?;
let spinner = start_spinner("Authenticating...")?;
let token_result = token_req
.request_async(&http_client)
.await
.into_diagnostic()
.wrap_err("Failed to exchange Client Credentials for Access Token");
match token_result {
Ok(res) => {
finish_spinner_success(&spinner, "Authentication successful!");
Ok(res.access_token().secret().clone())
}
Err(e) => {
finish_spinner_error(&spinner, "Authentication failed!");
Err(e)
}
}
}
}