mirror of
https://github.com/OMGeeky/yup-oauth2.git
synced 2025-12-26 16:27:25 +01:00
--all-features and --no-default-features
This adjusts the code and documentation for `--all-features` and `--no-default-features` to work correctly. With `--no-default-features` no `DefaultAuthenticator` is made available. Users are in control of picking the `Connector` they want to use, and are not forced to stomach a dependency on `rustls` or `hyper-tls` if their TLS implementation of choice doesn't happen to match one of the two. To indicate this, the unstable `doc_cfg` feature is used to build documentation on docs.rs. That way the generated documentation has notices on these types that look as such: > This is supported on crate features hyper-rustls or hyper-tls only. Additionally this functionality is tested via additional coverage in the Actions' CI.
This commit is contained in:
29
.github/workflows/test.yml
vendored
29
.github/workflows/test.yml
vendored
@@ -4,17 +4,44 @@ name: Actions CI
|
||||
|
||||
jobs:
|
||||
build_and_test:
|
||||
name: yup-oauth2
|
||||
name: yup-oauth2
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
profile: minimal
|
||||
default: true
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --all-features
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
args: --no-default-features --tests --examples
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --examples
|
||||
|
||||
doc:
|
||||
name: yup-oauth2
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: nightly
|
||||
profile: minimal
|
||||
default: true
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: doc
|
||||
args: --all-features
|
||||
env:
|
||||
RUSTDOCFLAGS: --cfg yup_oauth2_docsrs
|
||||
|
||||
13
Cargo.toml
13
Cargo.toml
@@ -10,6 +10,15 @@ keywords = ["google", "oauth", "v2"]
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2018"
|
||||
|
||||
[[example]]
|
||||
name = "custom_flow"
|
||||
required-features = ["hyper-rustls"]
|
||||
|
||||
[[test]]
|
||||
name = "tests"
|
||||
required-features = ["hyper-rustls"]
|
||||
|
||||
|
||||
[features]
|
||||
default = ["hyper-rustls"]
|
||||
|
||||
@@ -41,3 +50,7 @@ webbrowser = "0.5"
|
||||
|
||||
[workspace]
|
||||
members = ["examples/test-installed/", "examples/test-svc-acct/", "examples/test-device/", "examples/service_account", "examples/drive_example"]
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
rustdoc-args = ["--cfg", "yup_oauth2_docsrs"]
|
||||
|
||||
@@ -132,6 +132,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
enum StorageType {
|
||||
Memory,
|
||||
Disk(PathBuf),
|
||||
}
|
||||
|
||||
/// Configure an Authenticator using the builder pattern.
|
||||
pub struct AuthenticatorBuilder<C, F> {
|
||||
hyper_client_builder: C,
|
||||
@@ -157,6 +162,11 @@ pub struct AuthenticatorBuilder<C, F> {
|
||||
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,
|
||||
@@ -180,6 +190,11 @@ impl InstalledFlowAuthenticator {
|
||||
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> {
|
||||
@@ -200,6 +215,11 @@ impl DeviceFlowAuthenticator {
|
||||
pub struct ServiceAccountAuthenticator;
|
||||
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> {
|
||||
@@ -250,6 +270,11 @@ impl<C, F> AuthenticatorBuilder<C, F> {
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))]
|
||||
#[cfg_attr(
|
||||
yup_oauth2_docsrs,
|
||||
doc(cfg(any(feature = "hyper-rustls", feature = "hyper-tls")))
|
||||
)]
|
||||
fn with_auth_flow(auth_flow: F) -> AuthenticatorBuilder<DefaultHyperClient, F> {
|
||||
AuthenticatorBuilder {
|
||||
hyper_client_builder: DefaultHyperClient,
|
||||
@@ -484,27 +509,58 @@ pub trait HyperClientBuilder {
|
||||
fn build_hyper_client(self) -> hyper::Client<Self::Connector>;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "hyper-tls"))]
|
||||
impl<C> HyperClientBuilder for hyper::Client<C>
|
||||
where
|
||||
C: hyper::client::connect::Connect + Clone + Send + Sync + 'static,
|
||||
{
|
||||
type Connector = C;
|
||||
|
||||
fn build_hyper_client(self) -> hyper::Client<C> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[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(feature = "hyper-tls")]
|
||||
|
||||
#[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(not(feature = "hyper-tls"))]
|
||||
#[cfg(feature = "hyper-rustls")]
|
||||
type Connector = hyper_rustls::HttpsConnector<hyper::client::connect::HttpConnector>;
|
||||
#[cfg(feature = "hyper-tls")]
|
||||
#[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(not(feature = "hyper-tls"))]
|
||||
#[cfg(feature = "hyper-rustls")]
|
||||
let connector = hyper_rustls::HttpsConnector::with_native_roots();
|
||||
#[cfg(feature = "hyper-tls")]
|
||||
#[cfg(all(not(feature = "hyper-rustls"), feature = "hyper-tls"))]
|
||||
let connector = hyper_tls::HttpsConnector::new();
|
||||
|
||||
hyper::Client::builder()
|
||||
@@ -537,8 +593,8 @@ enum StorageType {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))]
|
||||
fn ensure_send_sync() {
|
||||
fn is_send_sync<T: Send + Sync>() {}
|
||||
is_send_sync::<Authenticator<<DefaultHyperClient as HyperClientBuilder>::Connector>>()
|
||||
|
||||
@@ -8,8 +8,9 @@ use std::io;
|
||||
use serde::Deserialize;
|
||||
|
||||
/// Error returned by the authorization server.
|
||||
/// https://tools.ietf.org/html/rfc6749#section-5.2
|
||||
/// https://tools.ietf.org/html/rfc8628#section-3.5
|
||||
///
|
||||
/// <https://tools.ietf.org/html/rfc6749#section-5.2>
|
||||
/// <https://tools.ietf.org/html/rfc8628#section-3.5>
|
||||
#[derive(Deserialize, Debug, PartialEq, Eq)]
|
||||
pub struct AuthError {
|
||||
/// Error code from the server.
|
||||
|
||||
@@ -60,7 +60,9 @@ where
|
||||
})
|
||||
}
|
||||
|
||||
/// cf. https://developers.google.com/identity/protocols/OAuth2InstalledApp#choosingredirecturi
|
||||
/// Method by which the user agent return token to this application.
|
||||
///
|
||||
/// cf. <https://developers.google.com/identity/protocols/OAuth2InstalledApp#choosingredirecturi>
|
||||
pub enum InstalledFlowReturnMethod {
|
||||
/// Involves showing a URL to the user and asking to copy a code from their browser
|
||||
/// (default)
|
||||
@@ -71,8 +73,8 @@ pub enum InstalledFlowReturnMethod {
|
||||
}
|
||||
|
||||
/// InstalledFlowImpl provides tokens for services that follow the "Installed" OAuth flow. (See
|
||||
/// https://www.oauth.com/oauth2-servers/authorization/,
|
||||
/// https://developers.google.com/identity/protocols/OAuth2InstalledApp).
|
||||
/// <https://www.oauth.com/oauth2-servers/authorization/>,
|
||||
/// <https://developers.google.com/identity/protocols/OAuth2InstalledApp>).
|
||||
pub struct InstalledFlow {
|
||||
pub(crate) app_secret: ApplicationSecret,
|
||||
pub(crate) method: InstalledFlowReturnMethod,
|
||||
|
||||
@@ -69,6 +69,8 @@
|
||||
//! ```
|
||||
//!
|
||||
#![deny(missing_docs)]
|
||||
#![cfg_attr(yup_oauth2_docsrs, feature(doc_cfg))]
|
||||
|
||||
pub mod authenticator;
|
||||
pub mod authenticator_delegate;
|
||||
mod device;
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
//! This module provides a token source (`GetToken`) that obtains tokens for service accounts.
|
||||
//! This module provides a flow that obtains tokens for service accounts.
|
||||
//!
|
||||
//! Service accounts are usually used by software (i.e., non-human actors) to get access to
|
||||
//! resources. Currently, this module only works with RS256 JWTs, which makes it at least suitable for
|
||||
//! authentication with Google services.
|
||||
//! resources. Currently, this module only works with RS256 JWTs, which makes it at least suitable
|
||||
//! for authentication with Google services.
|
||||
//!
|
||||
//! Resources:
|
||||
//! - [Using OAuth 2.0 for Server to Server
|
||||
@@ -9,7 +10,6 @@
|
||||
//! - [JSON Web Tokens](https://jwt.io/)
|
||||
//!
|
||||
//! Copyright (c) 2016 Google Inc (lewinb@google.com).
|
||||
//!
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::types::TokenInfo;
|
||||
@@ -54,8 +54,9 @@ fn decode_rsa_key(pem_pkcs8: &str) -> Result<PrivateKey, io::Error> {
|
||||
}
|
||||
}
|
||||
|
||||
/// JSON schema of secret service account key. You can obtain the key from
|
||||
/// the Cloud Console at https://console.cloud.google.com/.
|
||||
/// JSON schema of secret service account key.
|
||||
///
|
||||
/// You can obtain the key from the [Cloud Console](https://console.cloud.google.com/).
|
||||
///
|
||||
/// You can use `helpers::read_service_account_key()` as a quick way to read a JSON client
|
||||
/// secret into a ServiceAccountKey.
|
||||
@@ -210,12 +211,10 @@ impl ServiceAccountFlow {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::authenticator::HyperClientBuilder;
|
||||
use crate::helper::read_service_account_key;
|
||||
#[cfg(not(feature = "hyper-tls"))]
|
||||
use hyper_rustls::HttpsConnector;
|
||||
#[cfg(feature = "hyper-tls")]
|
||||
use hyper_tls::HttpsConnector;
|
||||
|
||||
// Valid but deactivated key.
|
||||
const TEST_PRIVATE_KEY_PATH: &'static str = "examples/Sanguine-69411a0c0eea.json";
|
||||
@@ -223,18 +222,13 @@ mod tests {
|
||||
// Uncomment this test to verify that we can successfully obtain tokens.
|
||||
//#[tokio::test]
|
||||
#[allow(dead_code)]
|
||||
#[cfg(any(feature = "hyper-rustls", feature = "hyper-tls"))]
|
||||
async fn test_service_account_e2e() {
|
||||
let key = read_service_account_key(TEST_PRIVATE_KEY_PATH)
|
||||
.await
|
||||
.unwrap();
|
||||
let acc = ServiceAccountFlow::new(ServiceAccountFlowOpts { key, subject: None }).unwrap();
|
||||
#[cfg(not(feature = "hyper-tls"))]
|
||||
let https = HttpsConnector::with_native_roots();
|
||||
#[cfg(feature = "hyper-tls")]
|
||||
let https = HttpsConnector::new();
|
||||
let client = hyper::Client::builder()
|
||||
.pool_max_idle_per_host(0)
|
||||
.build::<_, hyper::Body>(https);
|
||||
let client = crate::authenticator::DefaultHyperClient.build_hyper_client();
|
||||
println!(
|
||||
"{:?}",
|
||||
acc.token(&client, &["https://www.googleapis.com/auth/pubsub"])
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use yup_oauth2::{
|
||||
authenticator::Authenticator,
|
||||
authenticator::{DefaultAuthenticator, DefaultHyperClient, HyperClientBuilder},
|
||||
authenticator_delegate::{DeviceAuthResponse, DeviceFlowDelegate, InstalledFlowDelegate},
|
||||
error::{AuthError, AuthErrorCode},
|
||||
ApplicationSecret, DeviceFlowAuthenticator, Error, InstalledFlowAuthenticator,
|
||||
@@ -11,12 +11,7 @@ use std::path::PathBuf;
|
||||
use std::pin::Pin;
|
||||
|
||||
use httptest::{matchers::*, responders::json_encoded, Expectation, Server};
|
||||
use hyper::client::connect::HttpConnector;
|
||||
use hyper::Uri;
|
||||
#[cfg(not(feature = "hyper-tls"))]
|
||||
use hyper_rustls::HttpsConnector;
|
||||
#[cfg(feature = "hyper-tls")]
|
||||
use hyper_tls::HttpsConnector;
|
||||
use url::form_urlencoded;
|
||||
|
||||
/// Utility function for parsing json. Useful in unit tests. Simply wrap the
|
||||
@@ -27,7 +22,7 @@ macro_rules! parse_json {
|
||||
}
|
||||
}
|
||||
|
||||
async fn create_device_flow_auth(server: &Server) -> Authenticator<HttpsConnector<HttpConnector>> {
|
||||
async fn create_device_flow_auth(server: &Server) -> DefaultAuthenticator {
|
||||
let app_secret: ApplicationSecret = parse_json!({
|
||||
"client_id": "902216714886-k2v9uei3p1dk6h686jbsn9mo96tnbvto.apps.googleusercontent.com",
|
||||
"project_id": "yup-test-243420",
|
||||
@@ -166,7 +161,7 @@ async fn create_installed_flow_auth(
|
||||
server: &Server,
|
||||
method: InstalledFlowReturnMethod,
|
||||
filename: Option<PathBuf>,
|
||||
) -> Authenticator<HttpsConnector<HttpConnector>> {
|
||||
) -> DefaultAuthenticator {
|
||||
let app_secret: ApplicationSecret = parse_json!({
|
||||
"client_id": "902216714886-k2v9uei3p1dk6h686jbsn9mo96tnbvto.apps.googleusercontent.com",
|
||||
"project_id": "yup-test-243420",
|
||||
@@ -176,7 +171,7 @@ async fn create_installed_flow_auth(
|
||||
"client_secret": "iuMPN6Ne1PD7cos29Tk9rlqH",
|
||||
"redirect_uris": ["urn:ietf:wg:oauth:2.0:oob","http://localhost"],
|
||||
});
|
||||
struct FD(hyper::Client<HttpsConnector<HttpConnector>>);
|
||||
struct FD(hyper::Client<<DefaultHyperClient as HyperClientBuilder>::Connector>);
|
||||
impl InstalledFlowDelegate for FD {
|
||||
/// Depending on need_code, return the pre-set code or send the code to the server at
|
||||
/// the redirect_uri given in the url.
|
||||
@@ -219,13 +214,8 @@ async fn create_installed_flow_auth(
|
||||
}
|
||||
}
|
||||
|
||||
let mut builder =
|
||||
InstalledFlowAuthenticator::builder(app_secret, method).flow_delegate(Box::new(FD(
|
||||
#[cfg(not(feature = "hyper-tls"))]
|
||||
hyper::Client::builder().build(HttpsConnector::with_native_roots()),
|
||||
#[cfg(feature = "hyper-tls")]
|
||||
hyper::Client::builder().build(HttpsConnector::new()),
|
||||
)));
|
||||
let mut builder = InstalledFlowAuthenticator::builder(app_secret, method)
|
||||
.flow_delegate(Box::new(FD(DefaultHyperClient.build_hyper_client())));
|
||||
|
||||
builder = if let Some(filename) = filename {
|
||||
builder.persist_tokens_to_disk(filename)
|
||||
@@ -321,9 +311,7 @@ async fn test_installed_error() {
|
||||
assert!(format!("{}", tokr.unwrap_err()).contains("invalid_code"));
|
||||
}
|
||||
|
||||
async fn create_service_account_auth(
|
||||
server: &Server,
|
||||
) -> Authenticator<HttpsConnector<HttpConnector>> {
|
||||
async fn create_service_account_auth(server: &Server) -> DefaultAuthenticator {
|
||||
let key: ServiceAccountKey = parse_json!({
|
||||
"type": "service_account",
|
||||
"project_id": "yup-test-243420",
|
||||
|
||||
Reference in New Issue
Block a user