Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(flows): Implement flow for installed apps #21

Merged
merged 5 commits into from
May 20, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ build = "src/build.rs"
chrono = ">= 0.2"
log = ">= 0.3"
mime = ">= 0.1"
url = ">= 0.5"
hyper = ">= 0.8.0"
url = "= 0.5"
hyper = "0.8.0"
itertools = ">= 0.4"
serde = "0.6.0"
serde_json = "0.6.0"
Expand Down
13 changes: 12 additions & 1 deletion src/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,15 +136,26 @@ impl Token {
/// All known authentication types, for suitable constants
#[derive(Clone, Copy)]
pub enum FlowType {
/// [device authentication](https://developers.google.com/youtube/v3/guides/authentication#devices)
/// [device authentication](https://developers.google.com/youtube/v3/guides/authentication#devices). Only works
/// for certain scopes.
Device,
/// [installed app flow](https://developers.google.com/identity/protocols/OAuth2InstalledApp). Required
/// for Drive, Calendar, Gmail...; Requires user to paste a code from the browser.
InstalledInteractive,
/// Same as InstalledInteractive, but uses a redirect: The OAuth provider redirects the user's
/// browser to a web server that is running on localhost. This may not work as well with the
/// Windows Firewall, but is more comfortable otherwise. The integer describes which port to
/// bind to (default: 8080)
InstalledRedirect(u32),
}

impl AsRef<str> for FlowType {
/// Converts itself into a URL string
fn as_ref(&self) -> &'static str {
match *self {
FlowType::Device => "https://accounts.google.com/o/oauth2/device/code",
FlowType::InstalledInteractive => "https://accounts.google.com/o/oauth2/v2/auth",
FlowType::InstalledRedirect(_) => "https://accounts.google.com/o/oauth2/v2/auth",
}
}
}
Expand Down
50 changes: 48 additions & 2 deletions src/helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ use std::cmp::min;
use std::error::Error;
use std::fmt;
use std::convert::From;
use std::io;

use common::{Token, FlowType, ApplicationSecret};
use device::{PollInformation, RequestError, DeviceFlow, PollError};
use installed::{InstalledFlow, InstalledFlowReturnMethod};
use refresh::{RefreshResult, RefreshFlow};
use chrono::{DateTime, UTC, Local};
use std::time::Duration;
Expand Down Expand Up @@ -189,6 +191,26 @@ impl<D, S, C> Authenticator<D, S, C>
}
}


fn do_installed_flow(&mut self, scopes: &Vec<&str>) -> Result<Token, Box<Error>> {
let installed_type;

match self.flow_type {
FlowType::InstalledInteractive => {
installed_type = Some(InstalledFlowReturnMethod::Interactive)
}
FlowType::InstalledRedirect(port) => {
installed_type = Some(InstalledFlowReturnMethod::HTTPRedirect(port))
}
_ => installed_type = None,
}

let mut flow = InstalledFlow::new(self.client.borrow_mut(), installed_type);
flow.obtain_token(&mut self.delegate,
&self.secret,
scopes.iter())
}

fn retrieve_device_token(&mut self, scopes: &Vec<&str>) -> Result<Token, Box<Error>> {
let mut flow = DeviceFlow::new(self.client.borrow_mut());

Expand Down Expand Up @@ -343,6 +365,8 @@ impl<D, S, C> GetToken for Authenticator<D, S, C>
match
match self.flow_type {
FlowType::Device => self.retrieve_device_token(&scopes),
FlowType::InstalledInteractive => self.do_installed_flow(&scopes),
FlowType::InstalledRedirect(_) => self.do_installed_flow(&scopes),
}
{
Ok(token) => {
Expand Down Expand Up @@ -448,9 +472,31 @@ pub trait AuthenticatorDelegate {
/// * Will only be called if the Authenticator's flow_type is `FlowType::Device`.
fn present_user_code(&mut self, pi: &PollInformation) {
println!("Please enter {} at {} and grant access to this application",
pi.user_code, pi.verification_url);
pi.user_code,
pi.verification_url);
println!("Do not close this application until you either denied or granted access.");
println!("You have time until {}.", pi.expires_at.with_timezone(&Local));
println!("You have time until {}.",
pi.expires_at.with_timezone(&Local));
}

/// Only method currently used by the InstalledFlow.
/// We need the user to navigate to a URL using their browser and potentially paste back a code
/// (or maybe not). Whether they have to enter a code depends on the InstalledFlowReturnMethod
/// used.
fn present_user_url(&mut self, url: &String, need_code: bool) -> Option<String> {
if need_code {
println!("Please direct your browser to {}, follow the instructions and enter the \
code displayed here: ",
url);

let mut code = String::new();
io::stdin().read_line(&mut code).ok().map(|_| code)
} else {
println!("Please direct your browser to {} and follow the instructions displayed \
there.",
url);
None
}
}
}

Expand Down
Loading