1use std::future::Future;
8use std::pin::Pin;
9use std::sync::Arc;
10
11use base64::Engine;
12use http::header::{HeaderValue, AUTHORIZATION};
13use http::HeaderMap;
14
15#[derive(Clone)]
17pub enum Auth {
18 Bearer {
20 token: TokenSource,
22 },
23 Basic {
25 username: TokenSource,
27 password: TokenSource,
29 },
30 Custom {
32 prefix: String,
34 value: TokenSource,
36 },
37}
38
39#[derive(Clone)]
41pub enum TokenSource {
42 Static(String),
44 Fn(Arc<dyn Fn() -> Option<String> + Send + Sync>),
46 AsyncFn(Arc<dyn AsyncTokenProvider>),
48}
49
50pub trait AsyncTokenProvider: Send + Sync {
52 fn resolve(&self) -> Pin<Box<dyn Future<Output = Option<String>> + Send + '_>>;
54}
55
56impl<F, Fut> AsyncTokenProvider for F
57where
58 F: Send + Sync,
59 F: Fn() -> Fut,
60 Fut: Future<Output = Option<String>> + Send + 'static,
61{
62 fn resolve(&self) -> Pin<Box<dyn Future<Output = Option<String>> + Send + '_>> {
63 Box::pin((self)())
64 }
65}
66
67impl Auth {
68 pub fn bearer(token: impl Into<String>) -> Self {
70 Self::Bearer {
71 token: TokenSource::Static(token.into()),
72 }
73 }
74
75 pub fn bearer_fn(f: impl Fn() -> Option<String> + Send + Sync + 'static) -> Self {
85 Self::Bearer {
86 token: TokenSource::Fn(Arc::new(f)),
87 }
88 }
89
90 pub fn basic(username: impl Into<String>, password: impl Into<String>) -> Self {
92 Self::Basic {
93 username: TokenSource::Static(username.into()),
94 password: TokenSource::Static(password.into()),
95 }
96 }
97
98 pub async fn apply(&self, headers: &mut HeaderMap) -> crate::Result<()> {
100 match self {
101 Self::Bearer { token } => {
102 if let Some(value) = resolve_token(token).await? {
103 set_authorization(headers, format!("Bearer {value}"))?;
104 }
105 }
106 Self::Basic { username, password } => {
107 let user = resolve_token(username).await?;
108 let pass = resolve_token(password).await?;
109 if let (Some(u), Some(p)) = (user, pass) {
110 let encoded =
111 base64::engine::general_purpose::STANDARD.encode(format!("{u}:{p}"));
112 set_authorization(headers, format!("Basic {encoded}"))?;
113 }
114 }
115 Self::Custom { prefix, value } => {
116 if let Some(v) = resolve_token(value).await? {
117 set_authorization(headers, format!("{prefix} {v}"))?;
118 }
119 }
120 }
121 Ok(())
122 }
123}
124
125async fn resolve_token(source: &TokenSource) -> crate::Result<Option<String>> {
126 match source {
127 TokenSource::Static(s) => Ok(Some(s.clone())),
128 TokenSource::Fn(f) => Ok(f()),
129 TokenSource::AsyncFn(f) => Ok(f.resolve().await),
130 }
131}
132
133fn set_authorization(headers: &mut HeaderMap, value: String) -> crate::Result<()> {
134 let header_value = HeaderValue::from_str(&value)
135 .map_err(|e| crate::error::Error::Other(format!("invalid authorization header: {e}")))?;
136 headers.insert(AUTHORIZATION, header_value);
137 Ok(())
138}