1use std::sync::Arc;
2use std::time::Duration;
3
4use azure_core::credentials::{AccessToken, TokenCredential};
5use azure_identity::AzureCliCredential;
6use chrono::{DateTime, Utc};
7use serde::{Deserialize, Serialize};
8
9use crate::error::{AvError, Result};
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct AzureTempCredentials {
14 pub access_token: String,
15 pub expires_at: DateTime<Utc>,
16}
17
18pub struct AzureCredentialIssuer {
24 credential: Arc<AzureCliCredential>,
25}
26
27impl AzureCredentialIssuer {
28 pub fn new() -> Result<Self> {
31 let credential = AzureCliCredential::new(None).map_err(|e| {
32 AvError::Azure(format!(
33 "Failed to create Azure credential. Run `az login` first. Error: {}",
34 e
35 ))
36 })?;
37
38 Ok(Self { credential })
39 }
40
41 pub async fn issue(&self, _ttl: Duration) -> Result<AzureTempCredentials> {
46 let scopes = &["https://management.azure.com/.default"];
48
49 let token: AccessToken = self
50 .credential
51 .get_token(scopes, None)
52 .await
53 .map_err(|e| AvError::Azure(format!("Failed to get Azure access token: {}", e)))?;
54
55 let expires_at = DateTime::from_timestamp(
57 token.expires_on.unix_timestamp(),
58 0
59 ).unwrap_or_else(Utc::now);
60
61 Ok(AzureTempCredentials {
62 access_token: token.token.secret().to_string(),
63 expires_at,
64 })
65 }
66
67 pub async fn issue_for_scope(&self, scope: &str, _ttl: Duration) -> Result<AzureTempCredentials> {
69 let scopes = &[scope];
70
71 let token: AccessToken = self
72 .credential
73 .get_token(scopes, None)
74 .await
75 .map_err(|e| AvError::Azure(format!("Failed to get Azure access token for {}: {}", scope, e)))?;
76
77 let expires_at = DateTime::from_timestamp(
78 token.expires_on.unix_timestamp(),
79 0
80 ).unwrap_or_else(Utc::now);
81
82 Ok(AzureTempCredentials {
83 access_token: token.token.secret().to_string(),
84 expires_at,
85 })
86 }
87}
88