bitski_provider/
access_token_providers.rs

1use anyhow::Error;
2use oauth2::basic::BasicClient;
3use oauth2::{AuthUrl, ClientId, ClientSecret, Scope, TokenResponse, TokenUrl};
4use std::fmt::Debug;
5use web3::futures::future::BoxFuture;
6
7pub trait AccessTokenProvider: Debug {
8    fn get_access_token(&self) -> BoxFuture<'static, Result<String, Error>>;
9}
10
11#[derive(Clone, Debug)]
12pub struct ClientCredentialsAccessTokenProvider {
13    client: BasicClient,
14    scopes: Option<Vec<String>>,
15}
16
17impl ClientCredentialsAccessTokenProvider {
18    pub fn new(client_id: String, client_secret: String, scopes: Option<Vec<String>>) -> Self {
19        let client = BasicClient::new(
20            ClientId::new(client_id),
21            Some(ClientSecret::new(client_secret)),
22            AuthUrl::new("https://account.bitski.com/oauth2/auth".to_string()).unwrap(),
23            Some(TokenUrl::new("https://account.bitski.com/oauth2/token".to_string()).unwrap()),
24        );
25
26        Self { client, scopes }
27    }
28
29    async fn get_access_token(
30        client: BasicClient,
31        scopes: Option<Vec<Scope>>,
32    ) -> Result<String, Error> {
33        let response = match scopes {
34            Some(scopes) => {
35                client
36                    .exchange_client_credentials()
37                    .add_scopes(scopes)
38                    .request_async(oauth2::reqwest::async_http_client)
39                    .await
40            }
41            None => {
42                client
43                    .exchange_client_credentials()
44                    .request_async(oauth2::reqwest::async_http_client)
45                    .await
46            }
47        };
48        match response {
49            Ok(response) => Ok(response.access_token().secret().clone()),
50            Err(error) => {
51                #[cfg(feature = "tracing")]
52                tracing::warn!("Got an error exchanging client credentials: {:?}", error);
53                Err(error.into())
54            }
55        }
56    }
57}
58
59impl AccessTokenProvider for ClientCredentialsAccessTokenProvider {
60    fn get_access_token(&self) -> BoxFuture<'static, Result<String, Error>> {
61        let client = self.client.clone();
62        let scopes = self
63            .scopes
64            .as_ref()
65            .map(|scopes| scopes.iter().map(|s| Scope::new(s.to_string())).collect());
66        Box::pin(async move { Self::get_access_token(client, scopes).await })
67    }
68}
69
70impl AccessTokenProvider for String {
71    fn get_access_token(&self) -> BoxFuture<'static, Result<String, Error>> {
72        Box::pin(std::future::ready(Ok(self.clone())))
73    }
74}
75
76impl AccessTokenProvider for () {
77    fn get_access_token(&self) -> BoxFuture<'static, Result<String, Error>> {
78        Box::pin(std::future::ready(Err(Error::msg("Not signed in"))))
79    }
80}