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