Move providers to types
This commit is contained in:
parent
7ca874de6b
commit
fcd1945dba
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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",
|
||||
|
|
104
src/client.rs
104
src/client.rs
|
@ -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())
|
||||
|
|
95
src/lib.rs
95
src/lib.rs
|
@ -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;
|
||||
|
||||
|
|
|
@ -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" }
|
||||
}
|
Loading…
Reference in New Issue