diff --git a/Cargo.toml b/Cargo.toml index cd6c2d1..756d128 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ futures = "0.3" httptest = "0.11.1" env_logger = "0.7" tempfile = "3.1" +webbrowser = "0.5" [workspace] members = ["examples/test-installed/", "examples/test-svc-acct/", "examples/test-device/", "examples/service_account"] diff --git a/examples/custom_flow.rs b/examples/custom_flow.rs new file mode 100644 index 0000000..462e4fd --- /dev/null +++ b/examples/custom_flow.rs @@ -0,0 +1,64 @@ +//! Demonstrating how to create a custom Flow +//! here we open the browser for the user, making the use of InstalledAppFlow more convenient as +//! nothing has to be copy/pasted. Reason, the browser will open, the user accepts the requested +//! scope by clicking through e.g. the google oauth2, after this is done a local webserver started +//! by InstalledFlowAuthenticator will consume the token coming from the oauth2 server = no copy or +//! paste needed to continue with the operation. +use std::future::Future; +use std::pin::Pin; +use yup_oauth2::authenticator_delegate::{DefaultInstalledFlowDelegate, InstalledFlowDelegate}; + +/// async function to be pinned by the `present_user_url` method of the trait +/// we use the existing `DefaultInstalledFlowDelegate::present_user_url` method as a fallback for +/// when the browser did not open for example, the user still see's the URL. +async fn browser_user_url(url: &str, need_code: bool) -> Result { + if webbrowser::open(url).is_ok() { + println!("webbrowser was successfully opened."); + } + let def_delegate = DefaultInstalledFlowDelegate; + def_delegate.present_user_url(url, need_code).await +} + +/// our custom delegate struct we will implement a flow delegate trait for: +/// in this case we will implement the `InstalledFlowDelegated` trait +#[derive(Copy, Clone)] +struct InstalledFlowBrowserDelegate; + +/// here we implement only the present_user_url method with the added webbrowser opening +/// the other behaviour of the trait does not need to be changed. +impl InstalledFlowDelegate for InstalledFlowBrowserDelegate { + /// the actual presenting of URL and browser opening happens in the function defined above here + /// we only pin it + fn present_user_url<'a>( + &'a self, + url: &'a str, + need_code: bool, + ) -> Pin> + Send + 'a>> { + Box::pin(browser_user_url(url, need_code)) + } +} + +#[tokio::main] +async fn main() { + // Put your client secret in the working directory! + let sec = yup_oauth2::read_application_secret("client_secret.json") + .await + .expect("client secret couldn't be read."); + let auth = yup_oauth2::InstalledFlowAuthenticator::builder( + sec, + yup_oauth2::InstalledFlowReturnMethod::HTTPRedirect, + ) + .persist_tokens_to_disk("tokencache.json") + // use our custom flow delegate instead of default + .flow_delegate(Box::new(InstalledFlowBrowserDelegate)) + .build() + .await + .expect("InstalledFlowAuthenticator failed to build"); + + let scopes = &["https://www.googleapis.com/auth/drive.file"]; + + match auth.token(scopes).await { + Err(e) => println!("error: {:?}", e), + Ok(t) => println!("The token is {:?}", t), + } +}