use std::path::Path;
use hyper_rustls::HttpsConnector;
use hyper_util::client::legacy::Client;
use hyper_util::client::legacy::connect::HttpConnector;
use hyper_util::rt::TokioExecutor;
use yup_oauth2::authenticator::Authenticator;
use yup_oauth2::{
CustomHyperClientBuilder, InstalledFlowAuthenticator, InstalledFlowReturnMethod,
read_application_secret,
};
use crate::error::{Error, Result};
pub const SCOPES: [&str; 2] = [
"https://www.googleapis.com/auth/documents",
"https://www.googleapis.com/auth/drive.file",
];
pub type Connector = HttpsConnector<HttpConnector>;
pub type GoogleAuthenticator = Authenticator<Connector>;
pub fn https_connector() -> Result<Connector> {
let _ = rustls::crypto::ring::default_provider().install_default();
let connector = hyper_rustls::HttpsConnectorBuilder::new()
.with_native_roots()
.map_err(|err| Error::Auth(format!("load native TLS roots: {err}")))?
.https_or_http()
.enable_http1()
.enable_http2()
.build();
Ok(connector)
}
pub async fn authenticator(
credentials_path: &Path,
token_path: &Path,
) -> Result<GoogleAuthenticator> {
let secret = read_application_secret(credentials_path)
.await
.map_err(|err| {
Error::Auth(format!(
"read credentials {}: {err}",
credentials_path.display()
))
})?;
ensure_parent_dir(token_path)?;
let client = Client::builder(TokioExecutor::new()).build(https_connector()?);
InstalledFlowAuthenticator::with_client(
secret,
InstalledFlowReturnMethod::HTTPRedirect,
CustomHyperClientBuilder::from(client),
)
.persist_tokens_to_disk(token_path)
.build()
.await
.map_err(|err| Error::Auth(format!("build authenticator: {err}")))
}
fn ensure_parent_dir(path: &Path) -> Result<()> {
let Some(parent) = path.parent() else {
return Ok(());
};
std::fs::create_dir_all(parent).map_err(|source| Error::Io {
path: parent.to_path_buf(),
source,
})
}
pub async fn obtain_token(auth: &GoogleAuthenticator) -> Result<()> {
auth.token(&SCOPES)
.await
.map(|_token| ())
.map_err(|err| Error::Auth(format!("acquire token: {err}")))
}