Move providers to types

This commit is contained in:
Curtis McEnroe 2015-12-01 21:36:51 -05:00
parent 7ca874de6b
commit fcd1945dba
6 changed files with 104 additions and 143 deletions

View File

@ -1,10 +1,10 @@
extern crate inth_oauth2;
use std::io;
use inth_oauth2::Client;
use inth_oauth2::{Client, GitHub};
fn main() {
let client = Client::github(
let client = Client::<GitHub>::new(
Default::default(),
"01774654cd9a6051e478",
"9f14d16d95d605e715ec1a9aecec220d2565fd5c",

View File

@ -1,10 +1,10 @@
extern crate inth_oauth2;
use std::io;
use inth_oauth2::Client;
use inth_oauth2::{Client, Google};
fn main() {
let client = Client::google(
let client = Client::<Google>::new(
Default::default(),
"143225766783-ip2d9qv6sdr37276t77luk6f7bhd6bj5.apps.googleusercontent.com",
"3kZ5WomzHFlN2f_XbhkyPd3o",

View File

@ -1,10 +1,10 @@
extern crate inth_oauth2;
use std::io;
use inth_oauth2::Client;
use inth_oauth2::{Client, Imgur};
fn main() {
let client = Client::imgur(
let client = Client::<Imgur>::new(
Default::default(),
"505c8ca804230e0",
"c898d8cf28404102752b2119a3a1c6aab49899c8",

View File

@ -1,10 +1,12 @@
use std::io::Read;
use std::marker::PhantomData;
use chrono::{UTC, Duration};
use hyper::{self, header, mime};
use rustc_serialize::json;
use url::{Url, form_urlencoded};
use super::Provider;
use super::{TokenPair, AccessTokenType, AccessToken, RefreshToken};
use super::error::{Error, Result, OAuth2Error, OAuth2ErrorCode};
@ -13,15 +15,32 @@ use super::error::{Error, Result, OAuth2Error, OAuth2ErrorCode};
/// Performs HTTP requests using the provided `hyper::Client`.
///
/// See [RFC6749 section 4.1](http://tools.ietf.org/html/rfc6749#section-4.1).
pub struct Client {
pub struct Client<P: Provider> {
http_client: hyper::Client,
auth_uri: String,
token_uri: String,
client_id: String,
client_secret: String,
redirect_uri: Option<String>,
provider: PhantomData<P>,
}
impl<P: Provider> Client<P> {
/// Creates an OAuth 2.0 client.
pub fn new<S>(
http_client: hyper::Client,
client_id: S,
client_secret: S,
redirect_uri: Option<S>
) -> Self where S: Into<String> {
Client {
http_client: http_client,
client_id: client_id.into(),
client_secret: client_secret.into(),
redirect_uri: redirect_uri.map(Into::into),
provider: PhantomData,
}
}
}
#[derive(RustcDecodable)]
@ -76,81 +95,12 @@ impl Into<OAuth2Error> for ErrorResponse {
}
}
macro_rules! site_constructors {
(
$(
#[$attr:meta]
$ident:ident => ($auth_uri:expr, $token_uri:expr)
),*
) => {
$(
#[$attr]
pub fn $ident<S>(
http_client: hyper::Client,
client_id: S,
client_secret: S,
redirect_uri: Option<S>
) -> Self where S: Into<String> {
Client {
http_client: http_client,
auth_uri: String::from($auth_uri),
token_uri: String::from($token_uri),
client_id: client_id.into(),
client_secret: client_secret.into(),
redirect_uri: redirect_uri.map(Into::into),
}
}
)*
}
}
impl Client {
/// Creates an OAuth 2.0 client.
pub fn new<S>(
http_client: hyper::Client,
auth_uri: S,
token_uri: S,
client_id: S,
client_secret: S,
redirect_uri: Option<S>
) -> Self where S: Into<String> {
Client {
http_client: http_client,
auth_uri: auth_uri.into(),
token_uri: token_uri.into(),
client_id: client_id.into(),
client_secret: client_secret.into(),
redirect_uri: redirect_uri.map(Into::into),
}
}
site_constructors!{
#[doc = "Creates a Google OAuth 2.0 client.\n\nSee [Using OAuth 2.0 to Access Google APIs](https://developers.google.com/identity/protocols/OAuth2)."]
google => (
"https://accounts.google.com/o/oauth2/auth",
"https://accounts.google.com/o/oauth2/token"
),
#[doc = "Creates a GitHub OAuth 2.0 client.\n\nSee [OAuth, GitHub API](https://developer.github.com/v3/oauth/)."]
github => (
"https://github.com/login/oauth/authorize",
"https://github.com/login/oauth/access_token"
),
#[doc = "Creates an Imgur OAuth 2.0 client.\n\n See [OAuth 2.0, Imgur](https://api.imgur.com/oauth2)."]
imgur => (
"https://api.imgur.com/oauth2/authorize",
"https://api.imgur.com/oauth2/token"
)
}
}
impl Client {
impl<P: Provider> Client<P> {
/// Constructs an authorization request URI.
///
/// See [RFC6749 section 4.1.1](http://tools.ietf.org/html/rfc6749#section-4.1.1).
pub fn auth_uri(&self, scope: Option<&str>, state: Option<&str>) -> Result<String> {
let mut uri = try!(Url::parse(&self.auth_uri));
let mut uri = try!(Url::parse(P::auth_uri()));
let mut query_pairs = vec![
("response_type", "code"),
@ -166,7 +116,7 @@ impl Client {
query_pairs.push(("state", state));
}
uri.set_query_from_pairs(query_pairs.iter());
uri.set_query_from_pairs(query_pairs);
Ok(uri.serialize())
}
@ -194,7 +144,7 @@ impl Client {
fn token_post(&self, body_pairs: Vec<(&str, &str)>) -> Result<TokenPair> {
let post_body = form_urlencoded::serialize(body_pairs);
let request = self.http_client.post(&self.token_uri)
let request = self.http_client.post(P::token_uri())
.header(self.auth_header())
.header(self.accept_header())
.header(header::ContentType::form_url_encoded())

View File

@ -1,6 +1,6 @@
//! # "It's not that hard" OAuth2 Client
//!
//! OAuth2 really isn't that hard, you know?
//! OAuth 2.0 really isn't that hard, you know?
//!
//! Implementation of [RFC6749](http://tools.ietf.org/html/rfc6749).
//!
@ -11,15 +11,22 @@
//!
//! ## Providers
//!
//! `inth_oauth2` can be used with any OAuth 2.0 provider, but provides defaults for a few common
//! ones.
//! `inth_oauth2` supports the following OAuth 2.0 providers:
//!
//! ### Google
//! - `Google`
//! - `GitHub`
//! - `Imgur`
//!
//! Support for others can be added by implementing the `Provider` trait.
//!
//! ## Examples
//!
//! ### Creating a client
//!
//! ```
//! use inth_oauth2::Client as OAuth2;
//! use inth_oauth2::{Client, Google};
//!
//! let auth = OAuth2::google(
//! let client = Client::<Google>::new(
//! Default::default(),
//! "CLIENT_ID",
//! "CLIENT_SECRET",
@ -27,77 +34,42 @@
//! );
//! ```
//!
//! ### GitHub
//! ### Constructing an authorization URI
//!
//! ```
//! use inth_oauth2::Client as OAuth2;
//!
//! let auth = OAuth2::github(Default::default(), "CLIENT_ID", "CLIENT_SECRET", None);
//! # use inth_oauth2::{Client, Google};
//! # let client = Client::<Google>::new(Default::default(), "", "", None);
//! let auth_uri = client.auth_uri(Some("scope"), Some("state")).unwrap();
//! ```
//!
//! ### Imgur
//!
//! ```
//! use inth_oauth2::Client as OAuth2;
//!
//! let auth = OAuth2::imgur(Default::default(), "CLIENT_ID", "CLIENT_SECRET", None);
//! ```
//!
//! ### Other
//!
//! An authorization URI and a token URI are required.
//!
//! ```
//! use inth_oauth2::Client as OAuth2;
//!
//! let auth = OAuth2::new(
//! Default::default(),
//! "https://example.com/oauth2/auth",
//! "https://example.com/oauth2/token",
//! "CLIENT_ID",
//! "CLIENT_SECRET",
//! None
//! );
//! ```
//!
//! ## Constructing an authorization URI
//!
//! Direct the user to an authorization URI to have them authorize your application.
//!
//! ```
//! # use inth_oauth2::Client as OAuth2;
//! # let auth = OAuth2::google(Default::default(), "", "", None);
//! let auth_uri = auth.auth_uri(Some("scope"), Some("state")).unwrap();
//! ```
//! ### Requesting an access token
//!
//! ## Requesting an access token
//!
//! Using a code obtained from the redirect of the authorization URI, request an access token.
//! Request an access token using a code obtained from the redirect of the authorization URI.
//!
//! ```no_run
//! # use inth_oauth2::Client as OAuth2;
//! # let auth = OAuth2::google(Default::default(), "", "", None);
//! # use inth_oauth2::{Client, Google};
//! # let client = Client::<Google>::new(Default::default(), "", "", None);
//! # let code = String::new();
//! let token_pair = auth.request_token(&code).unwrap();
//! let token_pair = client.request_token(&code).unwrap();
//! println!("{}", token_pair.access.token);
//! ```
//!
//! ## Refreshing an access token
//!
//! Refresh the access token when it has expired.
//! ### Refreshing an access token
//!
//! ```no_run
//! # use inth_oauth2::Client as OAuth2;
//! # let auth = OAuth2::google(Default::default(), "", "", None);
//! # let mut token_pair = auth.request_token("").unwrap();
//! # use inth_oauth2::{Client, Google};
//! # let client = Client::<Google>::new(Default::default(), "", "", None);
//! # let mut token_pair = client.request_token("").unwrap();
//! if token_pair.expired() {
//! if let Some(refresh) = token_pair.refresh {
//! token_pair = auth.refresh_token(refresh, None).unwrap();
//! token_pair = client.refresh_token(refresh, None).unwrap();
//! }
//! }
//! ```
//!
//! ## Using bearer access tokens
//! ### Using bearer access tokens
//!
//! If the obtained token is of the `Bearer` type, a Hyper `Authorization` header can be created
//! from it.
@ -106,9 +78,9 @@
//! # extern crate hyper;
//! # extern crate inth_oauth2;
//! # fn main() {
//! # use inth_oauth2::Client as OAuth2;
//! # let auth = OAuth2::google(Default::default(), "", "", None);
//! # let token_pair = auth.request_token("").unwrap();
//! # use inth_oauth2::{Client, Google};
//! # let client = Client::<Google>::new(Default::default(), "", "", None);
//! # let mut token_pair = client.request_token("").unwrap();
//! let client = hyper::Client::new();
//! let res = client.get("https://example.com/resource")
//! .header(token_pair.to_bearer_header().unwrap())
@ -117,7 +89,7 @@
//! # }
//! ```
//!
//! ## Persisting tokens
//! ### Persisting tokens
//!
//! `TokenPair` implements `Encodable` and `Decodable` from `rustc_serialize`, so can be persisted
//! as JSON.
@ -154,6 +126,9 @@ extern crate url;
pub use client::Client;
pub mod client;
pub use provider::{Provider, Google, GitHub, Imgur};
pub mod provider;
pub use token::{TokenPair, AccessTokenType, AccessToken, RefreshToken};
pub mod token;

36
src/provider.rs Normal file
View File

@ -0,0 +1,36 @@
/// An OAuth 2.0 provider.
pub trait Provider {
/// The authorization endpoint URI.
fn auth_uri() -> &'static str;
/// The token endpoint URI.
fn token_uri() -> &'static str;
}
/// Google OAuth 2.0 provider.
///
/// See [Using OAuth 2.0 to Access Google
/// APIs](https://developers.google.com/identity/protocols/OAuth2).
pub struct Google;
impl Provider for Google {
fn auth_uri() -> &'static str { "https://accounts.google.com/o/oauth2/auth" }
fn token_uri() -> &'static str { "https://accounts.google.com/o/oauth2/token" }
}
/// GitHub OAuth 2.0 provider.
///
/// See [OAuth, GitHub API](https://developer.github.com/v3/oauth/).
pub struct GitHub;
impl Provider for GitHub {
fn auth_uri() -> &'static str { "https://github.com/login/oauth/authorize" }
fn token_uri() -> &'static str { "https://github.com/login/oauth/access_token" }
}
/// Imgur OAuth 2.0 provider.
///
/// See [OAuth 2.0, Imgur](https://api.imgur.com/oauth2).
pub struct Imgur;
impl Provider for Imgur {
fn auth_uri() -> &'static str { "https://api.imgur.com/oauth2/authorize" }
fn token_uri() -> &'static str { "https://api.imgur.com/oauth2/token" }
}