1use async_trait::async_trait;
2use bytes::Bytes;
3
4use crate::error::Error;
5use crate::http::header::AUTHORIZATION;
6use crate::http::{HeaderValue, Request};
7use crate::types::SensitiveString;
8
9#[async_trait]
11pub trait TokenProvider: Send + Sync {
12 async fn access_token(&self) -> Result<String, Error>;
14
15 async fn refresh(&self) -> bool {
18 false
19 }
20}
21
22#[derive(Debug, Clone)]
25pub struct StaticTokenProvider {
26 pub token: SensitiveString,
28}
29
30impl StaticTokenProvider {
31 pub fn new(token: impl Into<SensitiveString>) -> StaticTokenProvider {
33 StaticTokenProvider {
34 token: token.into(),
35 }
36 }
37}
38
39#[async_trait]
40impl TokenProvider for StaticTokenProvider {
41 async fn access_token(&self) -> Result<String, Error> {
42 if self.token.is_empty() {
43 Err(Error::auth("no token configured"))
44 } else {
45 Ok(self.token.expose().to_string())
46 }
47 }
48}
49
50#[async_trait]
53impl<P: TokenProvider + ?Sized> TokenProvider for std::sync::Arc<P> {
54 async fn access_token(&self) -> Result<String, Error> {
55 (**self).access_token().await
56 }
57
58 async fn refresh(&self) -> bool {
59 (**self).refresh().await
60 }
61}
62
63#[async_trait]
66pub trait AuthStrategy: Send + Sync {
67 async fn authenticate(&self, request: &mut Request<Bytes>) -> Result<(), Error>;
69
70 async fn refresh(&self) -> bool {
72 false
73 }
74}
75
76pub struct BearerAuth<P: TokenProvider> {
78 provider: P,
79}
80
81impl<P: TokenProvider> BearerAuth<P> {
82 pub fn new(provider: P) -> BearerAuth<P> {
84 BearerAuth { provider }
85 }
86}
87
88#[async_trait]
89impl<P: TokenProvider> AuthStrategy for BearerAuth<P> {
90 async fn authenticate(&self, request: &mut Request<Bytes>) -> Result<(), Error> {
91 let token = self.provider.access_token().await?;
92 let value = HeaderValue::from_str(&format!("Bearer {token}"))
93 .map_err(|_| Error::auth("access token is not a valid header value"))?;
94 request.headers_mut().insert(AUTHORIZATION, value);
95 Ok(())
96 }
97
98 async fn refresh(&self) -> bool {
99 self.provider.refresh().await
100 }
101}