google_apis_common/
auth.rs

1//! Authentication for Google API endpoints
2//!
3//! Allows users to provide custom authentication implementations to suite their needs.
4//!
5//! By default, [`GetToken`] is implemented for:
6//! - [`Authenticator`] : An authenticator which supports a variety of authentication methods
7//! - [`String`] : Plain OAuth2 token in String format
8//! - [`NoToken`] : No token, used for APIs which do not require a token
9//!
10//! # Usage
11//! [`GetToken`] instances are designed to be used with the Hub constructor provided by the
12//! generated APIs.
13//!
14//! If you intend to use the API libraries on client devices,
15//! [`Authenticator`] supports a variety of client-side authentication methods,
16//! and should be used to provide authentication.
17//!
18//! If you intend to use the API libraries server-side, with server-side client authentication,
19//! use the [`oauth2`] crate and convert the resulting [`AccessToken`] to [`String`].
20//!
21//! If you intend to use APIs which do not require authentication, use [`NoToken`].
22//!
23//! If you have custom authentication requirements, you can implement [`GetToken`] manually.
24//!
25//! # Example
26//! ```rust
27//! use std::future::Future;
28//! use std::pin::Pin;
29//!
30//! use google_apis_common::GetToken;
31//! use yup_oauth2::authenticator::Authenticator;
32//!
33//! #[derive(Clone)]
34//! struct AuthenticatorWithRetry<S> {
35//!     auth: Authenticator<S>,
36//!     retries: usize,
37//! }
38//!
39//! impl<C> GetToken for AuthenticatorWithRetry<C>
40//! where
41//!     C: hyper_util::client::legacy::connect::Connect + Clone + Send + Sync + 'static,
42//! {
43//!     fn get_token<'a>(
44//!         &'a self,
45//!         scopes: &'a [&str],
46//!     ) -> Pin<Box<dyn Future<Output = Result<Option<String>, Box<dyn std::error::Error + Send + Sync>>> + Send + 'a>> {
47//!         Box::pin(async move {
48//!             let mut auth_token = Ok(None);
49//!             for _ in 0..=self.retries {
50//!                 match self.auth.token(scopes).await {
51//!                     Ok(token) => {
52//!                         auth_token = Ok(token.token().map(|t| t.to_owned()));
53//!                         break;
54//!                     },
55//!                     Err(e) => auth_token = Err(e.into()),
56//!                 }
57//!             }
58//!             auth_token
59//!         })
60//!     }
61//! }
62//! ```
63//! [`oauth2`]: https://docs.rs/oauth2/latest/oauth2/
64//! [`AccessToken`]: https://docs.rs/oauth2/latest/oauth2/struct.AccessToken.html
65//! [`Authenticator`]: yup_oauth2::authenticator::Authenticator
66
67use std::future::Future;
68use std::pin::Pin;
69
70type GetTokenOutput<'a> = Pin<
71    Box<
72        dyn Future<Output = Result<Option<String>, Box<dyn std::error::Error + Send + Sync>>>
73            + Send
74            + 'a,
75    >,
76>;
77
78pub trait GetToken: GetTokenClone + Send + Sync {
79    /// Called whenever an API call requires authentication via an oauth2 token.
80    /// Returns `Ok(None)` if a token is not necessary - otherwise, returns an error
81    /// indicating the reason why a token could not be produced.
82    fn get_token<'a>(&'a self, _scopes: &'a [&str]) -> GetTokenOutput<'a>;
83}
84
85pub trait GetTokenClone {
86    fn clone_box(&self) -> Box<dyn GetToken>;
87}
88
89impl<T> GetTokenClone for T
90where
91    T: 'static + GetToken + Clone,
92{
93    fn clone_box(&self) -> Box<dyn GetToken> {
94        Box::new(self.clone())
95    }
96}
97
98impl Clone for Box<dyn GetToken> {
99    fn clone(&self) -> Box<dyn GetToken> {
100        self.clone_box()
101    }
102}
103
104impl GetToken for String {
105    fn get_token<'a>(&'a self, _scopes: &'a [&str]) -> GetTokenOutput<'a> {
106        Box::pin(async move { Ok(Some(self.clone())) })
107    }
108}
109
110/// In the event that the API endpoint does not require an OAuth2 token, `NoToken` should be
111/// provided to the hub to avoid specifying an authenticator.
112#[derive(Default, Clone)]
113pub struct NoToken;
114
115impl GetToken for NoToken {
116    fn get_token<'a>(&'a self, _scopes: &'a [&str]) -> GetTokenOutput<'a> {
117        Box::pin(async move { Ok(None) })
118    }
119}
120
121#[cfg(feature = "yup-oauth2")]
122impl<C> GetToken for yup_oauth2::authenticator::Authenticator<C>
123where
124    C: hyper_util::client::legacy::connect::Connect + Clone + Send + Sync + 'static,
125{
126    fn get_token<'a>(&'a self, scopes: &'a [&str]) -> GetTokenOutput<'a> {
127        Box::pin(async move {
128            self.token(scopes)
129                .await
130                .map(|t| t.token().map(|t| t.to_owned()))
131                .map_err(|e| e.into())
132        })
133    }
134}
135
136#[cfg(test)]
137mod test {
138    use super::*;
139
140    #[test]
141    fn dyn_get_token_is_send() {
142        fn with_send(_x: impl Send) {}
143
144        let mut gt = String::new();
145        let dgt: &mut dyn GetToken = &mut gt;
146        with_send(dgt);
147    }
148}