Files
yup-oauth2/src/authenticator.rs
Kyle Gentle c76ae18224 feat(Authenticator client): Accept custom connectors
Update Authenticator to accept clients with custom connectors, rather
than depending on the sealed hyper::client::connect::Connect trait, as recommended by hyper: https://docs.rs/hyper/0.13.8/src/hyper/client/connect/mod.rs.html#256-258

Closes #177.
2022-05-22 16:29:02 -04:00

862 lines
30 KiB
Rust

//! Module containing the core functionality for OAuth2 Authentication.
use crate::application_default_credentials::{
ApplicationDefaultCredentialsFlow, ApplicationDefaultCredentialsFlowOpts,
};
use crate::authenticator_delegate::{DeviceFlowDelegate, InstalledFlowDelegate};
use crate::authorized_user::{AuthorizedUserFlow, AuthorizedUserSecret};
use crate::device::DeviceFlow;
use crate::error::Error;
use crate::installed::{InstalledFlow, InstalledFlowReturnMethod};
use crate::refresh::RefreshFlow;
#[cfg(feature = "service_account")]
use crate::service_account::{self, ServiceAccountFlow, ServiceAccountFlowOpts, ServiceAccountKey};
use crate::storage::{self, Storage, TokenStorage};
use crate::types::{AccessToken, ApplicationSecret, TokenInfo};
use private::AuthFlow;
use futures::lock::Mutex;
use http::{Uri};
use hyper::client::connect::Connection;
use std::borrow::Cow;
use std::error::Error as StdError;
use std::fmt;
use std::io;
use std::path::PathBuf;
use std::sync::Arc;
use tokio::io::{AsyncRead, AsyncWrite};
use tower_service::Service;
struct InnerAuthenticator<S> {
hyper_client: hyper::Client<S>,
storage: Storage,
auth_flow: AuthFlow,
}
/// Authenticator is responsible for fetching tokens, handling refreshing tokens,
/// and optionally persisting tokens to disk.
#[derive(Clone)]
pub struct Authenticator<S> {
inner: Arc<InnerAuthenticator<S>>,
}
struct DisplayScopes<'a, T>(&'a [T]);
impl<'a, T> fmt::Display for DisplayScopes<'a, T>
where
T: AsRef<str>,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("[")?;
let mut iter = self.0.iter();
if let Some(first) = iter.next() {
f.write_str(first.as_ref())?;
for scope in iter {
f.write_str(", ")?;
f.write_str(scope.as_ref())?;
}
}
f.write_str("]")
}
}
impl<S> Authenticator<S>
where
S: Service<Uri> + Clone + Send + Sync + 'static,
S::Response: Connection + AsyncRead + AsyncWrite + Send + Unpin + 'static,
S::Future: Send + Unpin + 'static,
S::Error: Into<Box<dyn StdError + Send + Sync>>,
{
/// Return the current token for the provided scopes.
pub async fn token<'a, T>(&'a self, scopes: &'a [T]) -> Result<AccessToken, Error>
where
T: AsRef<str>,
{
self.find_token_info(scopes, /* force_refresh = */ false)
.await
.map(|info| info.into())
}
/// Return a token for the provided scopes, but don't reuse cached tokens. Instead,
/// always fetch a new token from the OAuth server.
pub async fn force_refreshed_token<'a, T>(
&'a self,
scopes: &'a [T],
) -> Result<AccessToken, Error>
where
T: AsRef<str>,
{
self.find_token_info(scopes, /* force_refresh = */ true)
.await
.map(|info| info.into())
}
/// Return the current ID token for the provided scopes, if any
pub async fn id_token<'a, T>(&'a self, scopes: &'a [T]) -> Result<Option<String>, Error>
where
T: AsRef<str>,
{
self.find_token_info(scopes, /* force_refresh = */ false)
.await
.map(|info| info.id_token)
}
/// Return a cached token or fetch a new one from the server.
async fn find_token_info<'a, T>(
&'a self,
scopes: &'a [T],
force_refresh: bool,
) -> Result<TokenInfo, Error>
where
T: AsRef<str>,
{
log::debug!(
"access token requested for scopes: {}",
DisplayScopes(scopes)
);
let hashed_scopes = storage::ScopeSet::from(scopes);
match (
self.inner.storage.get(hashed_scopes).await,
self.inner.auth_flow.app_secret(),
) {
(Some(t), _) if !t.is_expired() && !force_refresh => {
// unexpired token found
log::debug!("found valid token in cache: {:?}", t);
Ok(t)
}
(
Some(TokenInfo {
refresh_token: Some(refresh_token),
..
}),
Some(app_secret),
) => {
// token is expired but has a refresh token.
let token_info_result = RefreshFlow::refresh_token(
&self.inner.hyper_client,
app_secret,
&refresh_token,
)
.await;
let token_info = if let Ok(token_info) = token_info_result {
token_info
} else {
// token refresh failed.
self.inner
.auth_flow
.token(&self.inner.hyper_client, scopes)
.await?
};
self.inner
.storage
.set(hashed_scopes, token_info.clone())
.await?;
Ok(token_info)
}
_ => {
// no token in the cache or the token returned can't be refreshed.
let token_info = self
.inner
.auth_flow
.token(&self.inner.hyper_client, scopes)
.await?;
self.inner
.storage
.set(hashed_scopes, token_info.clone())
.await?;
Ok(token_info)
}
}
}
}
/// Configure an Authenticator using the builder pattern.
pub struct AuthenticatorBuilder<C, F> {
hyper_client_builder: C,
storage_type: StorageType,
auth_flow: F,
}
/// Create an authenticator that uses the installed flow.
/// ```
/// # #[cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))]
/// # async fn foo() {
/// # use yup_oauth2::InstalledFlowReturnMethod;
/// # let custom_flow_delegate = yup_oauth2::authenticator_delegate::DefaultInstalledFlowDelegate;
/// # let app_secret = yup_oauth2::read_application_secret("/tmp/foo").await.unwrap();
/// let authenticator = yup_oauth2::InstalledFlowAuthenticator::builder(
/// app_secret,
/// InstalledFlowReturnMethod::HTTPRedirect,
/// )
/// .build()
/// .await
/// .expect("failed to create authenticator");
/// # }
/// ```
pub struct InstalledFlowAuthenticator;
impl InstalledFlowAuthenticator {
/// Use the builder pattern to create an Authenticator that uses the installed flow.
#[cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))]
#[cfg_attr(
yup_oauth2_docsrs,
doc(cfg(any(feature = "hyper-rustls", feature = "hyper-tls")))
)]
pub fn builder(
app_secret: ApplicationSecret,
method: InstalledFlowReturnMethod,
) -> AuthenticatorBuilder<DefaultHyperClient, InstalledFlow> {
Self::with_client(app_secret, method, DefaultHyperClient)
}
/// Construct a new Authenticator that uses the installed flow and the provided http client.
pub fn with_client<C>(
app_secret: ApplicationSecret,
method: InstalledFlowReturnMethod,
client: C,
) -> AuthenticatorBuilder<C, InstalledFlow> {
AuthenticatorBuilder::new(InstalledFlow::new(app_secret, method), client)
}
}
/// Create an authenticator that uses the device flow.
/// ```
/// # #[cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))]
/// # async fn foo() {
/// # let app_secret = yup_oauth2::read_application_secret("/tmp/foo").await.unwrap();
/// let authenticator = yup_oauth2::DeviceFlowAuthenticator::builder(app_secret)
/// .build()
/// .await
/// .expect("failed to create authenticator");
/// # }
/// ```
pub struct DeviceFlowAuthenticator;
impl DeviceFlowAuthenticator {
/// Use the builder pattern to create an Authenticator that uses the device flow.
#[cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))]
#[cfg_attr(
yup_oauth2_docsrs,
doc(cfg(any(feature = "hyper-rustls", feature = "hyper-tls")))
)]
pub fn builder(
app_secret: ApplicationSecret,
) -> AuthenticatorBuilder<DefaultHyperClient, DeviceFlow> {
Self::with_client(app_secret, DefaultHyperClient)
}
/// Construct a new Authenticator that uses the installed flow and the provided http client.
pub fn with_client<C>(
app_secret: ApplicationSecret,
client: C,
) -> AuthenticatorBuilder<C, DeviceFlow> {
AuthenticatorBuilder::new(DeviceFlow::new(app_secret), client)
}
}
/// Create an authenticator that uses a service account.
/// ```
/// # #[cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))]
/// # async fn foo() {
/// # let service_account_key = yup_oauth2::read_service_account_key("/tmp/foo").await.unwrap();
/// let authenticator = yup_oauth2::ServiceAccountAuthenticator::builder(service_account_key)
/// .build()
/// .await
/// .expect("failed to create authenticator");
/// # }
/// ```
#[cfg(feature = "service_account")]
pub struct ServiceAccountAuthenticator;
#[cfg(feature = "service_account")]
impl ServiceAccountAuthenticator {
/// Use the builder pattern to create an Authenticator that uses a service account.
#[cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))]
#[cfg_attr(
yup_oauth2_docsrs,
doc(cfg(any(feature = "hyper-rustls", feature = "hyper-tls")))
)]
pub fn builder(
service_account_key: ServiceAccountKey,
) -> AuthenticatorBuilder<DefaultHyperClient, ServiceAccountFlowOpts> {
Self::with_client(service_account_key, DefaultHyperClient)
}
/// Construct a new Authenticator that uses the installed flow and the provided http client.
pub fn with_client<C>(
service_account_key: ServiceAccountKey,
client: C,
) -> AuthenticatorBuilder<C, ServiceAccountFlowOpts> {
AuthenticatorBuilder::new(
ServiceAccountFlowOpts {
key: service_account::FlowOptsKey::Key(service_account_key),
subject: None,
},
client,
)
}
}
/// Create an authenticator that uses a application default credentials.
/// ```
/// # #[cfg(all(any(feature = "hyper-rustls", feature = "hyper-tls"), feature = "service_account"))]
/// # async fn foo() {
/// # use yup_oauth2::ApplicationDefaultCredentialsAuthenticator;
/// # use yup_oauth2::ApplicationDefaultCredentialsFlowOpts;
/// # use yup_oauth2::authenticator::ApplicationDefaultCredentialsTypes;
///
/// let opts = ApplicationDefaultCredentialsFlowOpts::default();
/// let authenticator = match ApplicationDefaultCredentialsAuthenticator::builder(opts).await {
/// ApplicationDefaultCredentialsTypes::InstanceMetadata(auth) => auth
/// .build()
/// .await
/// .expect("Unable to create instance metadata authenticator"),
/// ApplicationDefaultCredentialsTypes::ServiceAccount(auth) => auth
/// .build()
/// .await
/// .expect("Unable to create service account authenticator"),
/// };
/// # }
/// ```
pub struct ApplicationDefaultCredentialsAuthenticator;
impl ApplicationDefaultCredentialsAuthenticator {
/// Try to build ServiceAccountFlowOpts from the environment
#[cfg(feature = "service_account")]
pub async fn from_environment() -> Result<ServiceAccountFlowOpts, std::env::VarError> {
let key_path = std::env::var("GOOGLE_APPLICATION_CREDENTIALS")?;
Ok(ServiceAccountFlowOpts {
key: service_account::FlowOptsKey::Path(key_path.into()),
subject: None,
})
}
/// Use the builder pattern to deduce which model of authenticator should be used:
/// Service account one or GCE instance metadata kind
#[cfg(feature = "service_account")]
#[cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))]
#[cfg_attr(
yup_oauth2_docsrs,
doc(cfg(any(feature = "hyper-rustls", feature = "hyper-tls")))
)]
pub async fn builder(
opts: ApplicationDefaultCredentialsFlowOpts,
) -> ApplicationDefaultCredentialsTypes<DefaultHyperClient> {
Self::with_client(opts, DefaultHyperClient).await
}
/// Use the builder pattern to deduce which model of authenticator should be used and allow providing a hyper client
#[cfg(feature = "service_account")]
pub async fn with_client<C>(
opts: ApplicationDefaultCredentialsFlowOpts,
client: C,
) -> ApplicationDefaultCredentialsTypes<C>
where
C: HyperClientBuilder,
{
match ApplicationDefaultCredentialsAuthenticator::from_environment().await {
Ok(flow_opts) => {
let builder = AuthenticatorBuilder::new(flow_opts, client);
ApplicationDefaultCredentialsTypes::ServiceAccount(builder)
}
Err(_) => ApplicationDefaultCredentialsTypes::InstanceMetadata(
AuthenticatorBuilder::new(opts, client),
),
}
}
}
/// Types of authenticators provided by ApplicationDefaultCredentialsAuthenticator
pub enum ApplicationDefaultCredentialsTypes<C>
where
C: HyperClientBuilder,
{
/// Service account based authenticator signature
#[cfg(feature = "service_account")]
ServiceAccount(AuthenticatorBuilder<C, ServiceAccountFlowOpts>),
/// GCE Instance Metadata based authenticator signature
InstanceMetadata(AuthenticatorBuilder<C, ApplicationDefaultCredentialsFlowOpts>),
}
/// Create an authenticator that uses an authorized user credentials.
/// ```
/// # #[cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))]
/// # async fn foo() {
/// # use yup_oauth2::authenticator::AuthorizedUserAuthenticator;
/// # let secret = yup_oauth2::read_authorized_user_secret("/tmp/foo").await.unwrap();
/// let authenticator = yup_oauth2::AuthorizedUserAuthenticator::builder(secret)
/// .build()
/// .await
/// .expect("failed to create authenticator");
/// # }
/// ```
pub struct AuthorizedUserAuthenticator;
impl AuthorizedUserAuthenticator {
/// Use the builder pattern to create an Authenticator that uses an authorized user.
#[cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))]
#[cfg_attr(
yup_oauth2_docsrs,
doc(cfg(any(feature = "hyper-rustls", feature = "hyper-tls")))
)]
pub fn builder(
authorized_user_secret: AuthorizedUserSecret,
) -> AuthenticatorBuilder<DefaultHyperClient, AuthorizedUserFlow> {
Self::with_client(authorized_user_secret, DefaultHyperClient)
}
/// Construct a new Authenticator that uses the installed flow and the provided http client.
pub fn with_client<C>(
authorized_user_secret: AuthorizedUserSecret,
client: C,
) -> AuthenticatorBuilder<C, AuthorizedUserFlow> {
AuthenticatorBuilder::new(
AuthorizedUserFlow {
secret: authorized_user_secret,
},
client,
)
}
}
/// ## Methods available when building any Authenticator.
/// ```
/// # #[cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))]
/// # async fn foo() {
/// # let custom_hyper_client = hyper::Client::new();
/// # let app_secret = yup_oauth2::read_application_secret("/tmp/foo").await.unwrap();
/// let authenticator = yup_oauth2::DeviceFlowAuthenticator::builder(app_secret)
/// .hyper_client(custom_hyper_client)
/// .persist_tokens_to_disk("/tmp/tokenfile.json")
/// .build()
/// .await
/// .expect("failed to create authenticator");
/// # }
/// ```
impl<C, F> AuthenticatorBuilder<C, F> {
async fn common_build(
hyper_client_builder: C,
storage_type: StorageType,
auth_flow: AuthFlow,
) -> io::Result<Authenticator<C::Connector>>
where
C: HyperClientBuilder,
{
let hyper_client = hyper_client_builder.build_hyper_client();
let storage = match storage_type {
StorageType::Memory => Storage::Memory {
tokens: Mutex::new(storage::JSONTokens::new()),
},
StorageType::Disk(path) => Storage::Disk(storage::DiskStorage::new(path).await?),
StorageType::Custom(custom_store) => Storage::Custom(custom_store),
};
Ok(Authenticator {
inner: Arc::new(InnerAuthenticator {
hyper_client,
storage,
auth_flow,
}),
})
}
fn new(auth_flow: F, hyper_client_builder: C) -> AuthenticatorBuilder<C, F> {
AuthenticatorBuilder {
hyper_client_builder,
storage_type: StorageType::Memory,
auth_flow,
}
}
/// Use the provided token storage mechanism
pub fn with_storage(self, storage: Box<dyn TokenStorage>) -> Self {
AuthenticatorBuilder {
storage_type: StorageType::Custom(storage),
..self
}
}
/// Use the provided hyper client.
pub fn hyper_client<NewC>(
self,
hyper_client: hyper::Client<NewC>,
) -> AuthenticatorBuilder<hyper::Client<NewC>, F> {
AuthenticatorBuilder {
hyper_client_builder: hyper_client,
storage_type: self.storage_type,
auth_flow: self.auth_flow,
}
}
/// Persist tokens to disk in the provided filename.
pub fn persist_tokens_to_disk<P: Into<PathBuf>>(self, path: P) -> AuthenticatorBuilder<C, F> {
AuthenticatorBuilder {
storage_type: StorageType::Disk(path.into()),
..self
}
}
}
/// ## Methods available when building a device flow Authenticator.
/// ```
/// # #[cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))]
/// # async fn foo() {
/// # let custom_flow_delegate = yup_oauth2::authenticator_delegate::DefaultDeviceFlowDelegate;
/// # let app_secret = yup_oauth2::read_application_secret("/tmp/foo").await.unwrap();
/// let authenticator = yup_oauth2::DeviceFlowAuthenticator::builder(app_secret)
/// .device_code_url("foo")
/// .flow_delegate(Box::new(custom_flow_delegate))
/// .grant_type("foo")
/// .build()
/// .await
/// .expect("failed to create authenticator");
/// # }
/// ```
impl<C> AuthenticatorBuilder<C, DeviceFlow> {
/// Use the provided device code url.
pub fn device_code_url(self, url: impl Into<Cow<'static, str>>) -> Self {
AuthenticatorBuilder {
auth_flow: DeviceFlow {
device_code_url: url.into(),
..self.auth_flow
},
..self
}
}
/// Use the provided DeviceFlowDelegate.
pub fn flow_delegate(self, flow_delegate: Box<dyn DeviceFlowDelegate>) -> Self {
AuthenticatorBuilder {
auth_flow: DeviceFlow {
flow_delegate,
..self.auth_flow
},
..self
}
}
/// Use the provided grant type.
pub fn grant_type(self, grant_type: impl Into<Cow<'static, str>>) -> Self {
AuthenticatorBuilder {
auth_flow: DeviceFlow {
grant_type: grant_type.into(),
..self.auth_flow
},
..self
}
}
/// Create the authenticator.
pub async fn build(self) -> io::Result<Authenticator<C::Connector>>
where
C: HyperClientBuilder,
{
Self::common_build(
self.hyper_client_builder,
self.storage_type,
AuthFlow::DeviceFlow(self.auth_flow),
)
.await
}
}
/// ## Methods available when building an installed flow Authenticator.
/// ```
/// # #[cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))]
/// # async fn foo() {
/// # use yup_oauth2::InstalledFlowReturnMethod;
/// # let custom_flow_delegate = yup_oauth2::authenticator_delegate::DefaultInstalledFlowDelegate;
/// # let app_secret = yup_oauth2::read_application_secret("/tmp/foo").await.unwrap();
/// let authenticator = yup_oauth2::InstalledFlowAuthenticator::builder(
/// app_secret,
/// InstalledFlowReturnMethod::HTTPRedirect,
/// )
/// .flow_delegate(Box::new(custom_flow_delegate))
/// .build()
/// .await
/// .expect("failed to create authenticator");
/// # }
/// ```
impl<C> AuthenticatorBuilder<C, InstalledFlow> {
/// Use the provided InstalledFlowDelegate.
pub fn flow_delegate(self, flow_delegate: Box<dyn InstalledFlowDelegate>) -> Self {
AuthenticatorBuilder {
auth_flow: InstalledFlow {
flow_delegate,
..self.auth_flow
},
..self
}
}
/// Create the authenticator.
pub async fn build(self) -> io::Result<Authenticator<C::Connector>>
where
C: HyperClientBuilder,
{
Self::common_build(
self.hyper_client_builder,
self.storage_type,
AuthFlow::InstalledFlow(self.auth_flow),
)
.await
}
}
/// ## Methods available when building a service account authenticator.
/// ```
/// # #[cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))]
/// # async fn foo() {
/// # let service_account_key = yup_oauth2::read_service_account_key("/tmp/foo").await.unwrap();
/// let authenticator = yup_oauth2::ServiceAccountAuthenticator::builder(
/// service_account_key,
/// )
/// .subject("mysubject")
/// .build()
/// .await
/// .expect("failed to create authenticator");
/// # }
/// ```
#[cfg(feature = "service_account")]
impl<C> AuthenticatorBuilder<C, ServiceAccountFlowOpts> {
/// Use the provided subject.
pub fn subject(self, subject: impl Into<String>) -> Self {
AuthenticatorBuilder {
auth_flow: ServiceAccountFlowOpts {
subject: Some(subject.into()),
..self.auth_flow
},
..self
}
}
/// Create the authenticator.
pub async fn build(self) -> io::Result<Authenticator<C::Connector>>
where
C: HyperClientBuilder,
{
let service_account_auth_flow = ServiceAccountFlow::new(self.auth_flow).await?;
Self::common_build(
self.hyper_client_builder,
self.storage_type,
AuthFlow::ServiceAccountFlow(service_account_auth_flow),
)
.await
}
}
impl<C> AuthenticatorBuilder<C, ApplicationDefaultCredentialsFlowOpts> {
/// Create the authenticator.
pub async fn build(self) -> io::Result<Authenticator<C::Connector>>
where
C: HyperClientBuilder,
{
let application_default_credential_flow =
ApplicationDefaultCredentialsFlow::new(self.auth_flow);
Self::common_build(
self.hyper_client_builder,
self.storage_type,
AuthFlow::ApplicationDefaultCredentialsFlow(application_default_credential_flow),
)
.await
}
}
/// ## Methods available when building an authorized user flow Authenticator.
impl<C> AuthenticatorBuilder<C, AuthorizedUserFlow> {
/// Create the authenticator.
pub async fn build(self) -> io::Result<Authenticator<C::Connector>>
where
C: HyperClientBuilder,
{
Self::common_build(
self.hyper_client_builder,
self.storage_type,
AuthFlow::AuthorizedUserFlow(self.auth_flow),
)
.await
}
}
mod private {
use crate::application_default_credentials::ApplicationDefaultCredentialsFlow;
use crate::authorized_user::AuthorizedUserFlow;
use crate::authenticator::{AsyncRead, AsyncWrite, Connection, Service, StdError, Uri};
use crate::device::DeviceFlow;
use crate::error::Error;
use crate::installed::InstalledFlow;
#[cfg(feature = "service_account")]
use crate::service_account::ServiceAccountFlow;
use crate::types::{ApplicationSecret, TokenInfo};
pub enum AuthFlow {
DeviceFlow(DeviceFlow),
InstalledFlow(InstalledFlow),
#[cfg(feature = "service_account")]
ServiceAccountFlow(ServiceAccountFlow),
ApplicationDefaultCredentialsFlow(ApplicationDefaultCredentialsFlow),
AuthorizedUserFlow(AuthorizedUserFlow),
}
impl AuthFlow {
pub(crate) fn app_secret(&self) -> Option<&ApplicationSecret> {
match self {
AuthFlow::DeviceFlow(device_flow) => Some(&device_flow.app_secret),
AuthFlow::InstalledFlow(installed_flow) => Some(&installed_flow.app_secret),
#[cfg(feature = "service_account")]
AuthFlow::ServiceAccountFlow(_) => None,
AuthFlow::ApplicationDefaultCredentialsFlow(_) => None,
AuthFlow::AuthorizedUserFlow(_) => None,
}
}
pub(crate) async fn token<'a, S, T>(
&'a self,
hyper_client: &'a hyper::Client<S>,
scopes: &'a [T],
) -> Result<TokenInfo, Error>
where
T: AsRef<str>,
S: Service<Uri> + Clone + Send + Sync + 'static,
S::Response: Connection + AsyncRead + AsyncWrite + Send + Unpin + 'static,
S::Future: Send + Unpin + 'static,
S::Error: Into<Box<dyn StdError + Send + Sync>>,
{
match self {
AuthFlow::DeviceFlow(device_flow) => device_flow.token(hyper_client, scopes).await,
AuthFlow::InstalledFlow(installed_flow) => {
installed_flow.token(hyper_client, scopes).await
}
#[cfg(feature = "service_account")]
AuthFlow::ServiceAccountFlow(service_account_flow) => {
service_account_flow.token(hyper_client, scopes).await
}
AuthFlow::ApplicationDefaultCredentialsFlow(service_account_flow) => {
service_account_flow.token(hyper_client, scopes).await
}
AuthFlow::AuthorizedUserFlow(authorized_user_flow) => {
authorized_user_flow.token(hyper_client, scopes).await
}
}
}
}
}
/// A trait implemented for any hyper::Client as well as the DefaultHyperClient.
pub trait HyperClientBuilder {
/// The hyper connector that the resulting hyper client will use.
type Connector: Service<Uri> + Clone + Send + Sync + 'static;
/// Create a hyper::Client
fn build_hyper_client(self) -> hyper::Client<Self::Connector>;
/// Create a `hyper::Client` for tests (HTTPS not required)
#[doc(hidden)]
fn build_test_hyper_client(self) -> hyper::Client<Self::Connector>;
}
#[cfg(feature = "hyper-rustls")]
#[cfg_attr(
yup_oauth2_docsrs,
doc(cfg(any(feature = "hyper-rustls", feature = "hyper-tls")))
)]
/// Default authenticator type
pub type DefaultAuthenticator =
Authenticator<hyper_rustls::HttpsConnector<hyper::client::HttpConnector>>;
#[cfg(all(not(feature = "hyper-rustls"), feature = "hyper-tls"))]
#[cfg_attr(
yup_oauth2_docsrs,
doc(cfg(any(feature = "hyper-rustls", feature = "hyper-tls")))
)]
/// Default authenticator type
pub type DefaultAuthenticator =
Authenticator<hyper_tls::HttpsConnector<hyper::client::HttpConnector>>;
/// The builder value used when the default hyper client should be used.
#[cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))]
#[cfg_attr(
yup_oauth2_docsrs,
doc(cfg(any(feature = "hyper-rustls", feature = "hyper-tls")))
)]
pub struct DefaultHyperClient;
#[cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))]
#[cfg_attr(
yup_oauth2_docsrs,
doc(cfg(any(feature = "hyper-rustls", feature = "hyper-tls")))
)]
impl HyperClientBuilder for DefaultHyperClient {
#[cfg(feature = "hyper-rustls")]
type Connector = hyper_rustls::HttpsConnector<hyper::client::connect::HttpConnector>;
#[cfg(all(not(feature = "hyper-rustls"), feature = "hyper-tls"))]
type Connector = hyper_tls::HttpsConnector<hyper::client::connect::HttpConnector>;
fn build_hyper_client(self) -> hyper::Client<Self::Connector> {
#[cfg(feature = "hyper-rustls")]
let connector = hyper_rustls::HttpsConnectorBuilder::new()
.with_native_roots()
.https_only()
.enable_http1()
.enable_http2()
.build();
#[cfg(all(not(feature = "hyper-rustls"), feature = "hyper-tls"))]
let connector = hyper_tls::HttpsConnector::new();
hyper::Client::builder()
.pool_max_idle_per_host(0)
.build::<_, hyper::Body>(connector)
}
fn build_test_hyper_client(self) -> hyper::Client<Self::Connector> {
#[cfg(feature = "hyper-rustls")]
let connector = hyper_rustls::HttpsConnectorBuilder::new()
.with_native_roots()
.https_or_http()
.enable_http1()
.enable_http2()
.build();
#[cfg(all(not(feature = "hyper-rustls"), feature = "hyper-tls"))]
let connector = hyper_tls::HttpsConnector::new();
hyper::Client::builder()
.pool_max_idle_per_host(0)
.build::<_, hyper::Body>(connector)
}
}
impl<S> HyperClientBuilder for hyper::Client<S>
where
S: Service<Uri> + Clone + Send + Sync + 'static,
S::Response: Connection + AsyncRead + AsyncWrite + Send + Unpin + 'static,
S::Future: Send + Unpin + 'static,
S::Error: Into<Box<dyn StdError + Send + Sync>>,
{
type Connector = S;
fn build_hyper_client(self) -> hyper::Client<S> {
self
}
fn build_test_hyper_client(self) -> hyper::Client<Self::Connector> {
self
}
}
/// How should the acquired tokens be stored?
enum StorageType {
/// Store tokens in memory (and always log in again to acquire a new token on startup)
Memory,
/// Store tokens to disk in the given file. Warning, this may be insecure unless you configure your operating system to restrict read access to the file.
Disk(PathBuf),
/// Implement your own storage provider
Custom(Box<dyn TokenStorage>),
}
#[cfg(test)]
mod tests {
#[test]
#[cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))]
fn ensure_send_sync() {
use super::*;
fn is_send_sync<T: Send + Sync>() {}
is_send_sync::<Authenticator<<DefaultHyperClient as HyperClientBuilder>::Connector>>()
}
}