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

authorized_user flow #154

Closed
wants to merge 4 commits into from
Closed
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
46 changes: 46 additions & 0 deletions src/authenticator.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Module contianing the core functionality for OAuth2 Authentication.
use crate::authenticator_delegate::{DeviceFlowDelegate, InstalledFlowDelegate};
use crate::authorized_user::{AuthorizedUserFlow, AuthorizedUserFlowOpts, AuthorizedUserSecret};
use crate::device::DeviceFlow;
use crate::error::Error;
use crate::installed::{InstalledFlow, InstalledFlowReturnMethod};
Expand Down Expand Up @@ -210,6 +211,28 @@ impl ServiceAccountAuthenticator {
}
}

/// Create an authenticator that uses an authorized user credentials.
/// ```
/// # async fn foo() {
/// # 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.
pub fn builder(
authorized_user_secret: AuthorizedUserSecret,
) -> AuthenticatorBuilder<DefaultHyperClient, AuthorizedUserFlowOpts> {
AuthenticatorBuilder::<DefaultHyperClient, _>::with_auth_flow(AuthorizedUserFlowOpts {
secret: authorized_user_secret,
})
}
}

/// ## Methods available when building any Authenticator.
/// ```
/// # async fn foo() {
Expand Down Expand Up @@ -431,7 +454,25 @@ impl<C> AuthenticatorBuilder<C, ServiceAccountFlowOpts> {
}
}

/// ## Methods available when building an authorized user flow Authenticator.
impl<C> AuthenticatorBuilder<C, AuthorizedUserFlowOpts> {
/// Create the authenticator.
pub async fn build(self) -> io::Result<Authenticator<C::Connector>>
where
C: HyperClientBuilder,
{
let authorized_user_auth_flow = AuthorizedUserFlow::new(self.auth_flow)?;
Self::common_build(
self.hyper_client_builder,
self.storage_type,
AuthFlow::AuthorizedUserFlow(authorized_user_auth_flow),
)
.await
}
}

mod private {
use crate::authorized_user::AuthorizedUserFlow;
use crate::device::DeviceFlow;
use crate::error::Error;
use crate::installed::InstalledFlow;
Expand All @@ -442,6 +483,7 @@ mod private {
DeviceFlow(DeviceFlow),
InstalledFlow(InstalledFlow),
ServiceAccountFlow(ServiceAccountFlow),
AuthorizedUserFlow(AuthorizedUserFlow),
}

impl AuthFlow {
Expand All @@ -450,6 +492,7 @@ mod private {
AuthFlow::DeviceFlow(device_flow) => Some(&device_flow.app_secret),
AuthFlow::InstalledFlow(installed_flow) => Some(&installed_flow.app_secret),
AuthFlow::ServiceAccountFlow(_) => None,
AuthFlow::AuthorizedUserFlow(_) => None,
}
}

Expand All @@ -470,6 +513,9 @@ mod private {
AuthFlow::ServiceAccountFlow(service_account_flow) => {
service_account_flow.token(hyper_client, scopes).await
}
AuthFlow::AuthorizedUserFlow(authorized_user_flow) => {
authorized_user_flow.token(hyper_client, scopes).await
}
}
}
}
Expand Down
80 changes: 80 additions & 0 deletions src/authorized_user.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//! This module provides a token source (`GetToken`) that obtains tokens using user credentials
//! for use by software (i.e., non-human actors) to get access to Google services.
//!
//! Resources:
//! - [gcloud auth application-default login](https://cloud.google.com/sdk/gcloud/reference/auth/application-default/login)
//!
use crate::error::Error;
use crate::types::TokenInfo;
use hyper::header;
use serde::{Deserialize, Serialize};
use std::io;
use url::form_urlencoded;

const TOKEN_URI: &'static str = "https://accounts.google.com/o/oauth2/token";

/// JSON schema of authorized user secret. You can obtain it by
/// running on the client: `gcloud auth application-default login`.
///
/// You can use `helpers::read_authorized_user_secret()` to read a JSON file
/// into a `AuthorizedUserSecret`.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct AuthorizedUserSecret {
/// client_id
pub client_id: String,
/// client_secret
pub client_secret: String,
/// refresh_token
pub refresh_token: String,
#[serde(rename = "type")]
/// key_type
pub key_type: String,
}

pub struct AuthorizedUserFlowOpts {
pub(crate) secret: AuthorizedUserSecret,
}

/// AuthorizedUserFlow can fetch oauth tokens using an authorized user secret.
pub struct AuthorizedUserFlow {
secret: AuthorizedUserSecret,
}

impl AuthorizedUserFlow {
pub(crate) fn new(opts: AuthorizedUserFlowOpts) -> Result<Self, io::Error> {
Ok(AuthorizedUserFlow {
secret: opts.secret,
})
}

/// Send a request for a new Bearer token to the OAuth provider.
pub(crate) async fn token<C, T>(
&self,
hyper_client: &hyper::Client<C>,
_scopes: &[T],
) -> Result<TokenInfo, Error>
where
T: AsRef<str>,
C: hyper::client::connect::Connect + Clone + Send + Sync + 'static,
{
let req = form_urlencoded::Serializer::new(String::new())
.extend_pairs(&[
("client_id", self.secret.client_id.as_str()),
("client_secret", self.secret.client_secret.as_str()),
("refresh_token", self.secret.refresh_token.as_str()),
("grant_type", "refresh_token"),
])
.finish();

let request = hyper::Request::post(TOKEN_URI)
.header(header::CONTENT_TYPE, "application/x-www-form-urlencoded")
.body(hyper::Body::from(req))
.unwrap();

log::debug!("requesting token from authorized user: {:?}", request);
let (head, body) = hyper_client.request(request).await?.into_parts();
let body = hyper::body::to_bytes(body).await?;
log::debug!("received response; head: {:?}, body: {:?}", head, body);
TokenInfo::from_json(&body)
}
}
18 changes: 17 additions & 1 deletion src/helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
// Copyright (c) 2016 Google Inc ([email protected]).
//
// Refer to the project root for licensing information.
use crate::service_account::ServiceAccountKey;
use crate::types::{ApplicationSecret, ConsoleApplicationSecret};
use crate::{authorized_user::AuthorizedUserSecret, service_account::ServiceAccountKey};

use std::io;
use std::path::Path;
Expand Down Expand Up @@ -49,6 +49,22 @@ pub async fn read_service_account_key<P: AsRef<Path>>(path: P) -> io::Result<Ser
})
}

/// Read an authorized user secret from a JSON file. You can obtain it by running on the client:
/// `gcloud auth application-default login`.
/// The file should be on Windows in: `%APPDATA%/gcloud/application_default_credentials.json`
/// for other systems: `$HOME/.config/gcloud/application_default_credentials.json`.
pub async fn read_authorized_user_secret<P: AsRef<Path>>(
path: P,
) -> io::Result<AuthorizedUserSecret> {
let key = tokio::fs::read(path).await?;
serde_json::from_slice(&key).map_err(|e| {
io::Error::new(
io::ErrorKind::InvalidData,
format!("Bad authorized user secret: {}", e),
)
})
}

pub(crate) fn join<T>(pieces: &[T], separator: &str) -> String
where
T: AsRef<str>,
Expand Down
5 changes: 4 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
#![deny(missing_docs)]
pub mod authenticator;
pub mod authenticator_delegate;
mod authorized_user;
mod device;
pub mod error;
mod helper;
Expand All @@ -86,12 +87,14 @@ mod types;

#[doc(inline)]
pub use crate::authenticator::{
DeviceFlowAuthenticator, InstalledFlowAuthenticator, ServiceAccountAuthenticator,
AuthorizedUserAuthenticator, DeviceFlowAuthenticator, InstalledFlowAuthenticator,
ServiceAccountAuthenticator,
};

pub use crate::helper::*;
pub use crate::installed::InstalledFlowReturnMethod;

pub use crate::authorized_user::AuthorizedUserSecret;
pub use crate::service_account::ServiceAccountKey;

#[doc(inline)]
Expand Down