azure_identity/token_credentials/
specific_azure_credential.rs1use super::options;
2#[cfg(not(target_arch = "wasm32"))]
3use crate::AzureCliCredential;
4#[cfg(feature = "client_certificate")]
5use crate::ClientCertificateCredential;
6use crate::{
7 AppServiceManagedIdentityCredential, ClientSecretCredential, EnvironmentCredential,
8 TokenCredentialOptions, VirtualMachineManagedIdentityCredential, WorkloadIdentityCredential,
9};
10use azure_core::{
11 auth::{AccessToken, TokenCredential},
12 error::{ErrorKind, ResultExt},
13 Error,
14};
15use std::sync::Arc;
16
17pub const AZURE_CREDENTIAL_KIND: &str = "AZURE_CREDENTIAL_KIND";
18
19pub mod azure_credential_kinds {
20 pub const ENVIRONMENT: &str = "environment";
21 #[cfg(not(target_arch = "wasm32"))]
22 pub const AZURE_CLI: &str = "azurecli";
23 pub const VIRTUAL_MACHINE: &str = "virtualmachine";
24 pub const APP_SERVICE: &str = "appservice";
25 pub const CLIENT_SECRET: &str = "clientsecret";
26 pub const WORKLOAD_IDENTITY: &str = "workloadidentity";
27 #[cfg(feature = "client_certificate")]
28 pub const CLIENT_CERTIFICATE: &str = "clientcertificate";
29}
30
31pub fn create_credential() -> azure_core::Result<Arc<dyn TokenCredential>> {
34 create_credential_with_options(options::TokenCredentialOptions::default())
35}
36
37fn create_credential_with_options(
38 options: TokenCredentialOptions,
39) -> azure_core::Result<Arc<dyn TokenCredential>> {
40 let env = options.env();
41 match env.var(AZURE_CREDENTIAL_KIND) {
42 Ok(_) => SpecificAzureCredential::create(options)
43 .map(|cred| Arc::new(cred) as Arc<dyn TokenCredential>),
44 Err(_) => crate::DefaultAzureCredentialBuilder::default()
45 .with_options(options)
46 .build()
47 .map(|cred| Arc::new(cred) as Arc<dyn TokenCredential>),
48 }
49}
50
51pub fn create_specific_credential() -> azure_core::Result<Arc<dyn TokenCredential>> {
53 Ok(Arc::new(SpecificAzureCredential::create(
54 TokenCredentialOptions::default(),
55 )?))
56}
57
58#[derive(Debug)]
59pub(crate) enum SpecificAzureCredentialKind {
60 Environment(EnvironmentCredential),
61 #[cfg(not(target_arch = "wasm32"))]
62 AzureCli(AzureCliCredential),
63 VirtualMachine(VirtualMachineManagedIdentityCredential),
64 AppService(AppServiceManagedIdentityCredential),
65 ClientSecret(ClientSecretCredential),
66 WorkloadIdentity(WorkloadIdentityCredential),
67 #[cfg(feature = "client_certificate")]
68 ClientCertificate(ClientCertificateCredential),
69}
70
71#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
72#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
73impl TokenCredential for SpecificAzureCredentialKind {
74 async fn get_token(&self, scopes: &[&str]) -> azure_core::Result<AccessToken> {
75 match self {
76 SpecificAzureCredentialKind::Environment(credential) => {
77 credential.get_token(scopes).await
78 }
79 #[cfg(not(target_arch = "wasm32"))]
80 SpecificAzureCredentialKind::AzureCli(credential) => credential.get_token(scopes).await,
81 SpecificAzureCredentialKind::VirtualMachine(credential) => {
82 credential.get_token(scopes).await
83 }
84 SpecificAzureCredentialKind::AppService(credential) => {
85 credential.get_token(scopes).await
86 }
87 SpecificAzureCredentialKind::ClientSecret(credential) => {
88 credential.get_token(scopes).await
89 }
90 SpecificAzureCredentialKind::WorkloadIdentity(credential) => {
91 credential.get_token(scopes).await
92 }
93 #[cfg(feature = "client_certificate")]
94 SpecificAzureCredentialKind::ClientCertificate(credential) => {
95 credential.get_token(scopes).await
96 }
97 }
98 }
99
100 async fn clear_cache(&self) -> azure_core::Result<()> {
101 match self {
102 SpecificAzureCredentialKind::Environment(credential) => credential.clear_cache().await,
103 #[cfg(not(target_arch = "wasm32"))]
104 SpecificAzureCredentialKind::AzureCli(credential) => credential.clear_cache().await,
105 SpecificAzureCredentialKind::VirtualMachine(credential) => {
106 credential.clear_cache().await
107 }
108 SpecificAzureCredentialKind::AppService(credential) => credential.clear_cache().await,
109 SpecificAzureCredentialKind::ClientSecret(credential) => credential.clear_cache().await,
110 SpecificAzureCredentialKind::WorkloadIdentity(credential) => {
111 credential.clear_cache().await
112 }
113 #[cfg(feature = "client_certificate")]
114 SpecificAzureCredentialKind::ClientCertificate(credential) => {
115 credential.clear_cache().await
116 }
117 }
118 }
119}
120
121#[derive(Debug)]
122pub struct SpecificAzureCredential {
123 source: SpecificAzureCredentialKind,
124}
125
126impl SpecificAzureCredential {
127 pub fn create(options: TokenCredentialOptions) -> azure_core::Result<SpecificAzureCredential> {
128 let env = options.env();
129 let credential_type = env.var(AZURE_CREDENTIAL_KIND)?;
130 let source: SpecificAzureCredentialKind =
131 match credential_type.replace(' ', "").to_lowercase().as_str() {
133 azure_credential_kinds::ENVIRONMENT => EnvironmentCredential::create(options)
134 .map(SpecificAzureCredentialKind::Environment)
135 .with_context(ErrorKind::Credential, || {
136 format!(
137 "unable to create AZURE_CREDENTIAL_KIND of {}",
138 azure_credential_kinds::ENVIRONMENT
139 )
140 })?,
141 azure_credential_kinds::APP_SERVICE => {
142 AppServiceManagedIdentityCredential::create(options)
143 .map(SpecificAzureCredentialKind::AppService)
144 .with_context(ErrorKind::Credential, || {
145 format!(
146 "unable to create AZURE_CREDENTIAL_KIND of {}",
147 azure_credential_kinds::APP_SERVICE
148 )
149 })?
150 }
151 azure_credential_kinds::VIRTUAL_MACHINE => {
152 SpecificAzureCredentialKind::VirtualMachine(
153 VirtualMachineManagedIdentityCredential::new(options),
154 )
155 }
156 #[cfg(not(target_arch = "wasm32"))]
157 azure_credential_kinds::AZURE_CLI => AzureCliCredential::create()
158 .map(SpecificAzureCredentialKind::AzureCli)
159 .with_context(ErrorKind::Credential, || {
160 format!(
161 "unable to create AZURE_CREDENTIAL_KIND of {}",
162 azure_credential_kinds::AZURE_CLI
163 )
164 })?,
165 azure_credential_kinds::CLIENT_SECRET => ClientSecretCredential::create(options)
166 .map(SpecificAzureCredentialKind::ClientSecret)?,
167 azure_credential_kinds::WORKLOAD_IDENTITY => {
168 WorkloadIdentityCredential::create(options)
169 .map(SpecificAzureCredentialKind::WorkloadIdentity)
170 .with_context(ErrorKind::Credential, || {
171 format!(
172 "unable to create AZURE_CREDENTIAL_KIND of {}",
173 azure_credential_kinds::WORKLOAD_IDENTITY
174 )
175 })?
176 }
177 #[cfg(feature = "client_certificate")]
178 azure_credential_kinds::CLIENT_CERTIFICATE => {
179 ClientCertificateCredential::create(options)
180 .map(SpecificAzureCredentialKind::ClientCertificate)
181 .with_context(ErrorKind::Credential, || {
182 format!(
183 "unable to create AZURE_CREDENTIAL_KIND of {}",
184 azure_credential_kinds::CLIENT_CERTIFICATE
185 )
186 })?
187 }
188 _ => {
189 return Err(Error::with_message(ErrorKind::Credential, || {
190 format!("unknown AZURE_CREDENTIAL_KIND of {}", credential_type)
191 }))
192 }
193 };
194 Ok(Self { source })
195 }
196
197 #[cfg(test)]
198 pub(crate) fn source(&self) -> &SpecificAzureCredentialKind {
199 &self.source
200 }
201}
202
203#[cfg_attr(target_arch = "wasm32", async_trait::async_trait(?Send))]
204#[cfg_attr(not(target_arch = "wasm32"), async_trait::async_trait)]
205impl TokenCredential for SpecificAzureCredential {
206 async fn get_token(&self, scopes: &[&str]) -> azure_core::Result<AccessToken> {
207 self.source.get_token(scopes).await
208 }
209
210 async fn clear_cache(&self) -> azure_core::Result<()> {
211 self.source.clear_cache().await
212 }
213}
214
215#[cfg(test)]
216pub fn test_options(env_vars: &[(&str, &str)]) -> TokenCredentialOptions {
217 let env = crate::env::Env::from(env_vars);
218 let http_client = azure_core::new_http_client();
219 TokenCredentialOptions::new(env, http_client)
220}
221
222#[cfg(test)]
223mod tests {
224 use super::*;
225 use crate::EnvironmentCredentialKind;
226
227 #[test]
229 fn test_environment() -> azure_core::Result<()> {
230 let credential = SpecificAzureCredential::create(test_options(
231 &[
232 ("AZURE_CREDENTIAL_KIND", "environment"),
233 ("AZURE_TENANT_ID", "1"),
234 ("AZURE_CLIENT_ID", "2"),
235 ("AZURE_CLIENT_SECRET", "3"),
236 ][..],
237 ))?;
238 match credential.source() {
239 SpecificAzureCredentialKind::Environment(credential) => match credential.source() {
240 EnvironmentCredentialKind::ClientSecret(_) => {}
241 _ => panic!("expect client secret credential"),
242 },
243 _ => panic!("expected environment credential"),
244 }
245 Ok(())
246 }
247
248 #[test]
250 #[cfg(not(target_arch = "wasm32"))]
251 fn test_azure_cli() -> azure_core::Result<()> {
252 let credential = SpecificAzureCredential::create(test_options(
253 &[("AZURE_CREDENTIAL_KIND", "azurecli")][..],
254 ))?;
255 match credential.source() {
256 SpecificAzureCredentialKind::AzureCli(_) => {}
257 _ => panic!("expected azure cli credential"),
258 }
259 Ok(())
260 }
261
262 #[test]
264 #[cfg(not(target_arch = "wasm32"))]
265 fn test_azure_cli_naming() -> azure_core::Result<()> {
266 let credential = SpecificAzureCredential::create(test_options(
267 &[("AZURE_CREDENTIAL_KIND", "Azure CLI")][..],
268 ))?;
269 match credential.source() {
270 SpecificAzureCredentialKind::AzureCli(_) => {}
271 _ => panic!("expected azure cli credential"),
272 }
273 Ok(())
274 }
275
276 #[test]
278 fn test_virtual_machine() -> azure_core::Result<()> {
279 let credential = SpecificAzureCredential::create(test_options(
280 &[("AZURE_CREDENTIAL_KIND", "virtualmachine")][..],
281 ))?;
282 match credential.source() {
283 SpecificAzureCredentialKind::VirtualMachine(_) => {}
284 _ => panic!("expected virtual machine credential"),
285 }
286 Ok(())
287 }
288
289 #[test]
291 fn test_app_service() -> azure_core::Result<()> {
292 let credential = SpecificAzureCredential::create(test_options(
293 &[
294 ("AZURE_CREDENTIAL_KIND", "appservice"),
295 ("IDENTITY_ENDPOINT", "https://identityendpoint/token"),
296 ][..],
297 ))?;
298 match credential.source() {
299 SpecificAzureCredentialKind::AppService(_) => {}
300 _ => panic!("expected app service credential"),
301 }
302 Ok(())
303 }
304
305 #[test]
307 fn test_client_secret() -> azure_core::Result<()> {
308 let credential = SpecificAzureCredential::create(test_options(
309 &[
310 ("AZURE_CREDENTIAL_KIND", "clientsecret"),
311 ("AZURE_TENANT_ID", "1"),
312 ("AZURE_CLIENT_ID", "2"),
313 ("AZURE_CLIENT_SECRET", "3"),
314 ][..],
315 ))?;
316 match credential.source() {
317 SpecificAzureCredentialKind::ClientSecret(_) => {}
318 _ => panic!("expected client secret credential"),
319 }
320 Ok(())
321 }
322
323 #[test]
325 fn test_workload_identity() -> azure_core::Result<()> {
326 let credential = SpecificAzureCredential::create(test_options(
327 &[
328 ("AZURE_CREDENTIAL_KIND", "workloadidentity"),
329 ("AZURE_TENANT_ID", "1"),
330 ("AZURE_CLIENT_ID", "2"),
331 ("AZURE_FEDERATED_TOKEN", "3"),
332 ][..],
333 ))?;
334 match credential.source() {
335 SpecificAzureCredentialKind::WorkloadIdentity(_) => {}
336 _ => panic!("expected workload identity credential"),
337 }
338 Ok(())
339 }
340}