1use aws_credential_types::provider::{
9 error::TokenError, future, token::ProvideToken, token::Result,
10};
11use aws_smithy_types::error::display::DisplayErrorContext;
12use std::borrow::Cow;
13use tracing::Instrument;
14
15use crate::meta::{ProviderAttempt, ProviderChainError};
16
17#[cfg_attr(
26 feature = "sso",
27 doc = r#"
28# Examples
29
30```no_run
31# fn example() {
32use aws_config::meta::token::TokenProviderChain;
33use aws_config::profile::ProfileFileTokenProvider;
34use aws_credential_types::Token;
35
36let provider = TokenProviderChain::first_try("Profile", ProfileFileTokenProvider::builder().build())
37 .or_else("Static", Token::new("example", None));
38# }
39```
40"#
41)]
42#[derive(Debug)]
43pub struct TokenProviderChain {
44 providers: Vec<(Cow<'static, str>, Box<dyn ProvideToken>)>,
45}
46
47impl TokenProviderChain {
48 pub fn first_try(
50 name: impl Into<Cow<'static, str>>,
51 provider: impl ProvideToken + 'static,
52 ) -> Self {
53 TokenProviderChain {
54 providers: vec![(name.into(), Box::new(provider))],
55 }
56 }
57
58 pub fn or_else(
60 mut self,
61 name: impl Into<Cow<'static, str>>,
62 provider: impl ProvideToken + 'static,
63 ) -> Self {
64 self.providers.push((name.into(), Box::new(provider)));
65 self
66 }
67
68 #[cfg(feature = "sso")]
70 pub async fn or_default_provider(self) -> Self {
71 self.or_else(
72 "DefaultProviderChain",
73 crate::default_provider::token::default_provider().await,
74 )
75 }
76
77 #[cfg(feature = "sso")]
79 pub async fn default_provider() -> Self {
80 Self::first_try(
81 "DefaultProviderChain",
82 crate::default_provider::token::default_provider().await,
83 )
84 }
85
86 async fn token(&self) -> Result {
87 let mut attempts = Vec::with_capacity(self.providers.len());
88 for (name, provider) in &self.providers {
89 let span = tracing::debug_span!("token_provider_chain", provider = %name);
90 match provider.provide_token().instrument(span).await {
91 Ok(credentials) => {
92 tracing::debug!(provider = %name, "loaded access token");
93 return Ok(credentials);
94 }
95 Err(err @ TokenError::TokenNotLoaded(_)) => {
96 tracing::debug!(provider = %name, context = %DisplayErrorContext(&err), "provider in chain did not provide an access token");
97 attempts.push(ProviderAttempt::new(name.clone(), err));
98 }
99 Err(err) => {
100 tracing::warn!(provider = %name, error = %DisplayErrorContext(&err), "provider failed to provide an access token");
101 return Err(err);
102 }
103 }
104 }
105 Err(TokenError::not_loaded(ProviderChainError::new(attempts)))
106 }
107}
108
109impl ProvideToken for TokenProviderChain {
110 fn provide_token<'a>(&'a self) -> future::ProvideToken<'a>
111 where
112 Self: 'a,
113 {
114 future::ProvideToken::new(self.token())
115 }
116}