Move providers to types
This commit is contained in:
parent
7ca874de6b
commit
fcd1945dba
|
@ -1,10 +1,10 @@
|
||||||
extern crate inth_oauth2;
|
extern crate inth_oauth2;
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use inth_oauth2::Client;
|
use inth_oauth2::{Client, GitHub};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let client = Client::github(
|
let client = Client::<GitHub>::new(
|
||||||
Default::default(),
|
Default::default(),
|
||||||
"01774654cd9a6051e478",
|
"01774654cd9a6051e478",
|
||||||
"9f14d16d95d605e715ec1a9aecec220d2565fd5c",
|
"9f14d16d95d605e715ec1a9aecec220d2565fd5c",
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
extern crate inth_oauth2;
|
extern crate inth_oauth2;
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use inth_oauth2::Client;
|
use inth_oauth2::{Client, Google};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let client = Client::google(
|
let client = Client::<Google>::new(
|
||||||
Default::default(),
|
Default::default(),
|
||||||
"143225766783-ip2d9qv6sdr37276t77luk6f7bhd6bj5.apps.googleusercontent.com",
|
"143225766783-ip2d9qv6sdr37276t77luk6f7bhd6bj5.apps.googleusercontent.com",
|
||||||
"3kZ5WomzHFlN2f_XbhkyPd3o",
|
"3kZ5WomzHFlN2f_XbhkyPd3o",
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
extern crate inth_oauth2;
|
extern crate inth_oauth2;
|
||||||
|
|
||||||
use std::io;
|
use std::io;
|
||||||
use inth_oauth2::Client;
|
use inth_oauth2::{Client, Imgur};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let client = Client::imgur(
|
let client = Client::<Imgur>::new(
|
||||||
Default::default(),
|
Default::default(),
|
||||||
"505c8ca804230e0",
|
"505c8ca804230e0",
|
||||||
"c898d8cf28404102752b2119a3a1c6aab49899c8",
|
"c898d8cf28404102752b2119a3a1c6aab49899c8",
|
||||||
|
|
104
src/client.rs
104
src/client.rs
|
@ -1,10 +1,12 @@
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
use chrono::{UTC, Duration};
|
use chrono::{UTC, Duration};
|
||||||
use hyper::{self, header, mime};
|
use hyper::{self, header, mime};
|
||||||
use rustc_serialize::json;
|
use rustc_serialize::json;
|
||||||
use url::{Url, form_urlencoded};
|
use url::{Url, form_urlencoded};
|
||||||
|
|
||||||
|
use super::Provider;
|
||||||
use super::{TokenPair, AccessTokenType, AccessToken, RefreshToken};
|
use super::{TokenPair, AccessTokenType, AccessToken, RefreshToken};
|
||||||
use super::error::{Error, Result, OAuth2Error, OAuth2ErrorCode};
|
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`.
|
/// Performs HTTP requests using the provided `hyper::Client`.
|
||||||
///
|
///
|
||||||
/// See [RFC6749 section 4.1](http://tools.ietf.org/html/rfc6749#section-4.1).
|
/// 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,
|
http_client: hyper::Client,
|
||||||
|
|
||||||
auth_uri: String,
|
|
||||||
token_uri: String,
|
|
||||||
|
|
||||||
client_id: String,
|
client_id: String,
|
||||||
client_secret: String,
|
client_secret: String,
|
||||||
redirect_uri: Option<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)]
|
#[derive(RustcDecodable)]
|
||||||
|
@ -76,81 +95,12 @@ impl Into<OAuth2Error> for ErrorResponse {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! site_constructors {
|
impl<P: Provider> Client<P> {
|
||||||
(
|
|
||||||
$(
|
|
||||||
#[$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 {
|
|
||||||
/// Constructs an authorization request URI.
|
/// Constructs an authorization request URI.
|
||||||
///
|
///
|
||||||
/// See [RFC6749 section 4.1.1](http://tools.ietf.org/html/rfc6749#section-4.1.1).
|
/// 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> {
|
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![
|
let mut query_pairs = vec![
|
||||||
("response_type", "code"),
|
("response_type", "code"),
|
||||||
|
@ -166,7 +116,7 @@ impl Client {
|
||||||
query_pairs.push(("state", state));
|
query_pairs.push(("state", state));
|
||||||
}
|
}
|
||||||
|
|
||||||
uri.set_query_from_pairs(query_pairs.iter());
|
uri.set_query_from_pairs(query_pairs);
|
||||||
|
|
||||||
Ok(uri.serialize())
|
Ok(uri.serialize())
|
||||||
}
|
}
|
||||||
|
@ -194,7 +144,7 @@ impl Client {
|
||||||
|
|
||||||
fn token_post(&self, body_pairs: Vec<(&str, &str)>) -> Result<TokenPair> {
|
fn token_post(&self, body_pairs: Vec<(&str, &str)>) -> Result<TokenPair> {
|
||||||
let post_body = form_urlencoded::serialize(body_pairs);
|
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.auth_header())
|
||||||
.header(self.accept_header())
|
.header(self.accept_header())
|
||||||
.header(header::ContentType::form_url_encoded())
|
.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
|
//! # "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).
|
//! Implementation of [RFC6749](http://tools.ietf.org/html/rfc6749).
|
||||||
//!
|
//!
|
||||||
|
@ -11,15 +11,22 @@
|
||||||
//!
|
//!
|
||||||
//! ## Providers
|
//! ## Providers
|
||||||
//!
|
//!
|
||||||
//! `inth_oauth2` can be used with any OAuth 2.0 provider, but provides defaults for a few common
|
//! `inth_oauth2` supports the following OAuth 2.0 providers:
|
||||||
//! ones.
|
|
||||||
//!
|
//!
|
||||||
//! ### 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(),
|
//! Default::default(),
|
||||||
//! "CLIENT_ID",
|
//! "CLIENT_ID",
|
||||||
//! "CLIENT_SECRET",
|
//! "CLIENT_SECRET",
|
||||||
|
@ -27,77 +34,42 @@
|
||||||
//! );
|
//! );
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! ### GitHub
|
//! ### Constructing an authorization URI
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! use inth_oauth2::Client as OAuth2;
|
//! # use inth_oauth2::{Client, Google};
|
||||||
//!
|
//! # let client = Client::<Google>::new(Default::default(), "", "", None);
|
||||||
//! let auth = OAuth2::github(Default::default(), "CLIENT_ID", "CLIENT_SECRET", 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.
|
//! Direct the user to an authorization URI to have them authorize your application.
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ### Requesting an access token
|
||||||
//! # 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
|
//! Request an access token using a code obtained from the redirect of the authorization URI.
|
||||||
//!
|
|
||||||
//! Using a code obtained from the redirect of the authorization URI, request an access token.
|
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! # use inth_oauth2::Client as OAuth2;
|
//! # use inth_oauth2::{Client, Google};
|
||||||
//! # let auth = OAuth2::google(Default::default(), "", "", None);
|
//! # let client = Client::<Google>::new(Default::default(), "", "", None);
|
||||||
//! # let code = String::new();
|
//! # 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);
|
//! println!("{}", token_pair.access.token);
|
||||||
//! ```
|
//! ```
|
||||||
//!
|
//!
|
||||||
//! ## Refreshing an access token
|
//! ### Refreshing an access token
|
||||||
//!
|
|
||||||
//! Refresh the access token when it has expired.
|
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! # use inth_oauth2::Client as OAuth2;
|
//! # use inth_oauth2::{Client, Google};
|
||||||
//! # let auth = OAuth2::google(Default::default(), "", "", None);
|
//! # let client = Client::<Google>::new(Default::default(), "", "", None);
|
||||||
//! # let mut token_pair = auth.request_token("").unwrap();
|
//! # let mut token_pair = client.request_token("").unwrap();
|
||||||
//! if token_pair.expired() {
|
//! if token_pair.expired() {
|
||||||
//! if let Some(refresh) = token_pair.refresh {
|
//! 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
|
//! If the obtained token is of the `Bearer` type, a Hyper `Authorization` header can be created
|
||||||
//! from it.
|
//! from it.
|
||||||
|
@ -106,9 +78,9 @@
|
||||||
//! # extern crate hyper;
|
//! # extern crate hyper;
|
||||||
//! # extern crate inth_oauth2;
|
//! # extern crate inth_oauth2;
|
||||||
//! # fn main() {
|
//! # fn main() {
|
||||||
//! # use inth_oauth2::Client as OAuth2;
|
//! # use inth_oauth2::{Client, Google};
|
||||||
//! # let auth = OAuth2::google(Default::default(), "", "", None);
|
//! # let client = Client::<Google>::new(Default::default(), "", "", None);
|
||||||
//! # let token_pair = auth.request_token("").unwrap();
|
//! # let mut token_pair = client.request_token("").unwrap();
|
||||||
//! let client = hyper::Client::new();
|
//! let client = hyper::Client::new();
|
||||||
//! let res = client.get("https://example.com/resource")
|
//! let res = client.get("https://example.com/resource")
|
||||||
//! .header(token_pair.to_bearer_header().unwrap())
|
//! .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
|
//! `TokenPair` implements `Encodable` and `Decodable` from `rustc_serialize`, so can be persisted
|
||||||
//! as JSON.
|
//! as JSON.
|
||||||
|
@ -154,6 +126,9 @@ extern crate url;
|
||||||
pub use client::Client;
|
pub use client::Client;
|
||||||
pub mod client;
|
pub mod client;
|
||||||
|
|
||||||
|
pub use provider::{Provider, Google, GitHub, Imgur};
|
||||||
|
pub mod provider;
|
||||||
|
|
||||||
pub use token::{TokenPair, AccessTokenType, AccessToken, RefreshToken};
|
pub use token::{TokenPair, AccessTokenType, AccessToken, RefreshToken};
|
||||||
pub mod token;
|
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