bitski-provider 0.3.1

Bitski Web3 provider
Documentation
use anyhow::Error;
use oauth2::basic::BasicClient;
use oauth2::{AuthUrl, ClientId, ClientSecret, Scope, TokenResponse, TokenUrl};
use std::fmt::Debug;
use web3::futures::future::BoxFuture;

pub trait AccessTokenProvider: Debug {
    fn get_access_token(&self) -> BoxFuture<'static, Result<String, Error>>;
}

#[derive(Clone, Debug)]
pub struct ClientCredentialsAccessTokenProvider {
    client: BasicClient,
    scopes: Option<Vec<String>>,
}

impl ClientCredentialsAccessTokenProvider {
    pub fn new(client_id: String, client_secret: String, scopes: Option<Vec<String>>) -> Self {
        let client = BasicClient::new(
            ClientId::new(client_id),
            Some(ClientSecret::new(client_secret)),
            AuthUrl::new("https://account.bitski.com/oauth2/auth".to_string()).unwrap(),
            Some(TokenUrl::new("https://account.bitski.com/oauth2/token".to_string()).unwrap()),
        );

        Self { client, scopes }
    }

    async fn get_access_token(
        client: BasicClient,
        scopes: Option<Vec<Scope>>,
    ) -> Result<String, Error> {
        let response = match scopes {
            Some(scopes) => {
                client
                    .exchange_client_credentials()
                    .add_scopes(scopes)
                    .request_async(oauth2::reqwest::async_http_client)
                    .await
            }
            None => {
                client
                    .exchange_client_credentials()
                    .request_async(oauth2::reqwest::async_http_client)
                    .await
            }
        };
        match response {
            Ok(response) => Ok(response.access_token().secret().clone()),
            Err(error) => {
                #[cfg(feature = "tracing")]
                tracing::warn!("Got an error exchanging client credentials: {:?}", error);
                Err(error.into())
            }
        }
    }
}

impl AccessTokenProvider for ClientCredentialsAccessTokenProvider {
    fn get_access_token(&self) -> BoxFuture<'static, Result<String, Error>> {
        let client = self.client.clone();
        let scopes = self
            .scopes
            .as_ref()
            .map(|scopes| scopes.iter().map(|s| Scope::new(s.to_string())).collect());
        Box::pin(async move { Self::get_access_token(client, scopes).await })
    }
}

impl AccessTokenProvider for String {
    fn get_access_token(&self) -> BoxFuture<'static, Result<String, Error>> {
        Box::pin(std::future::ready(Ok(self.clone())))
    }
}

impl AccessTokenProvider for () {
    fn get_access_token(&self) -> BoxFuture<'static, Result<String, Error>> {
        Box::pin(std::future::ready(Err(Error::msg("Not signed in"))))
    }
}