use crate::auth::error::AuthError;
use crate::core::{CoreError, ServiceAccountKey};
use std::sync::Arc;
const IDENTITY_TOOLKIT_SCOPE: &str = "https://www.googleapis.com/auth/cloud-platform";
pub struct TokenProvider {
inner: Arc<dyn gcp_auth::TokenProvider>,
}
impl std::fmt::Debug for TokenProvider {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("TokenProvider").finish_non_exhaustive()
}
}
impl TokenProvider {
pub fn from_service_account(key: &ServiceAccountKey) -> Result<Self, AuthError> {
let json = serde_json::to_string(&ServiceAccountKeyJson::from(key))
.map_err(CoreError::Deserialize)?;
let account = gcp_auth::CustomServiceAccount::from_json(&json).map_err(|e| {
AuthError::Core(CoreError::Credentials(format!(
"failed to initialize service account credentials: {e}"
)))
})?;
Ok(Self {
inner: Arc::new(account),
})
}
pub async fn from_application_default() -> Result<Self, AuthError> {
let inner = gcp_auth::provider().await.map_err(|e| {
AuthError::Core(CoreError::Credentials(format!(
"failed to resolve Application Default Credentials: {e}"
)))
})?;
Ok(Self { inner })
}
pub async fn access_token(&self) -> Result<String, AuthError> {
let token = self
.inner
.token(&[IDENTITY_TOOLKIT_SCOPE])
.await
.map_err(|e| {
AuthError::Core(CoreError::Credentials(format!(
"failed to acquire an OAuth2 access token: {e}"
)))
})?;
Ok(token.as_str().to_string())
}
}
#[derive(serde::Serialize)]
struct ServiceAccountKeyJson<'a> {
r#type: &'static str,
client_email: &'a str,
private_key: &'a str,
private_key_id: &'a str,
project_id: &'a str,
}
impl<'a> From<&'a ServiceAccountKey> for ServiceAccountKeyJson<'a> {
fn from(key: &'a ServiceAccountKey) -> Self {
Self {
r#type: "service_account",
client_email: &key.client_email,
private_key: &key.private_key,
private_key_id: &key.private_key_id,
project_id: &key.project_id,
}
}
}