Skip to main content

tryaudex_core/
azure.rs

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/// Temporary Azure credentials (short-lived OAuth2 access token).
12#[derive(Debug, Clone, Serialize, Deserialize)]
13pub struct AzureTempCredentials {
14    pub access_token: String,
15    pub expires_at: DateTime<Utc>,
16}
17
18/// Issues short-lived Azure credentials via AzureCliCredential.
19/// 
20/// Works with Azure CLI credentials (`az login`).
21/// For other auth methods (service principals, managed identity), 
22/// use the appropriate credential type from azure_identity.
23pub struct AzureCredentialIssuer {
24    credential: Arc<AzureCliCredential>,
25}
26
27impl AzureCredentialIssuer {
28    /// Create a new issuer using AzureCliCredential.
29    /// Requires `az login` to have been run.
30    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    /// Get an access token for Azure Resource Manager.
42    /// 
43    /// The token is scoped to the ARM management API by default.
44    /// Azure tokens are typically valid for 1 hour.
45    pub async fn issue(&self, _ttl: Duration) -> Result<AzureTempCredentials> {
46        // Azure Resource Manager scope
47        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        // Convert azure_core's OffsetDateTime to chrono::DateTime
56        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    /// Get an access token for a specific Azure service scope.
68    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