Skip to main content

olai_http/azure/
mod.rs

1use std::sync::Arc;
2
3use futures::future::BoxFuture;
4
5use crate::service::make_service;
6use crate::token::TemporaryToken;
7use crate::{ClientOptions, CredentialProvider, RequestSigner, Result, RetryConfig, TokenProvider};
8
9mod builder;
10pub(crate) mod credential;
11mod sas;
12
13pub(crate) use self::credential::*;
14pub use builder::*;
15pub use credential::AzureCredential;
16pub use sas::{DEFAULT_TTL_SECS, generate_storage_key_sas, generate_user_delegation_sas};
17
18pub type AzureCredentialProvider = Arc<dyn CredentialProvider<Credential = AzureCredential>>;
19
20/// Configuration for Azure authentication
21#[derive(Debug, Clone)]
22pub struct AzureConfig {
23    pub credentials: AzureCredentialProvider,
24    pub retry_config: RetryConfig,
25    pub skip_signature: bool,
26    pub client_options: ClientOptions,
27}
28
29impl AzureConfig {
30    pub(crate) async fn get_credential(&self) -> Result<Option<Arc<AzureCredential>>> {
31        if self.skip_signature {
32            Ok(None)
33        } else {
34            Some(self.credentials.get_credential().await).transpose()
35        }
36    }
37}
38
39impl RequestSigner for AzureConfig {
40    fn sign<'a>(
41        &'a self,
42        req: reqwest::RequestBuilder,
43    ) -> BoxFuture<'a, Result<reqwest::RequestBuilder>> {
44        Box::pin(async move {
45            let credential = self.get_credential().await?;
46            Ok(req.with_azure_authorization(&credential))
47        })
48    }
49}
50
51/// Fetch a short-lived Azure storage bearer token using an OAuth2 client-secret flow.
52///
53/// Calls the Azure AD token endpoint for the given tenant and returns a
54/// `TemporaryToken<Arc<AzureCredential>>` scoped to Azure Storage
55/// (`https://storage.azure.com/.default`).
56///
57/// This is commonly used by data-platform servers to vend temporary Azure credentials
58/// for external locations backed by an `AzureServicePrincipal` credential.
59pub async fn fetch_client_secret_token(
60    tenant_id: &str,
61    client_id: String,
62    client_secret: String,
63    authority_host: Option<String>,
64) -> Result<TemporaryToken<Arc<AzureCredential>>> {
65    let provider = ClientSecretOAuthProvider::new_with_scope(
66        client_id,
67        client_secret,
68        tenant_id,
69        authority_host,
70        credential::AZURE_STORAGE_SCOPE,
71    );
72    let client = ClientOptions::default().client()?;
73    let service = make_service(client.clone(), None);
74    provider
75        .fetch_token(&client, &service, &RetryConfig::default())
76        .await
77}
78
79/// Fetch a short-lived Azure storage bearer token using workload identity (federated token file).
80///
81/// Reads the OIDC token from the given file path and exchanges it for an Azure AD bearer
82/// token via the client-credentials grant with a JWT client assertion.
83pub async fn fetch_workload_identity_token(
84    tenant_id: &str,
85    client_id: String,
86    federated_token_file: String,
87    authority_host: Option<String>,
88) -> Result<TemporaryToken<Arc<AzureCredential>>> {
89    let provider = WorkloadIdentityOAuthProvider::new(
90        client_id,
91        federated_token_file,
92        tenant_id,
93        authority_host,
94    );
95    let client = ClientOptions::default().client()?;
96    let service = make_service(client.clone(), None);
97    provider
98        .fetch_token(&client, &service, &RetryConfig::default())
99        .await
100}
101
102/// Fetch a short-lived Azure storage bearer token using IMDS managed identity.
103///
104/// Calls the Azure Instance Metadata Service (IMDS) endpoint and returns a
105/// bearer token scoped to Azure Storage.  Supports user-assigned identities via
106/// `client_id`, `object_id`, or `msi_res_id`.
107pub async fn fetch_managed_identity_token(
108    client_id: Option<String>,
109    object_id: Option<String>,
110    msi_res_id: Option<String>,
111) -> Result<TemporaryToken<Arc<AzureCredential>>> {
112    let provider = ImdsManagedIdentityProvider::new(client_id, object_id, msi_res_id, None);
113    let client = ClientOptions::default().with_allow_http(true).client()?;
114    let service = make_service(client.clone(), None);
115    provider
116        .fetch_token(&client, &service, &RetryConfig::default())
117        .await
118}