bitski_provider/
access_token_providers.rs1use 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}