Merge pull request #11 from programble/provider/google-web
Expiring/Refresh Lifetime split and Google Web Provider
This commit is contained in:
commit
0813240aea
|
@ -3,10 +3,10 @@ extern crate inth_oauth2;
|
||||||
use std::io;
|
use std::io;
|
||||||
|
|
||||||
use inth_oauth2::Client;
|
use inth_oauth2::Client;
|
||||||
use inth_oauth2::provider::Google;
|
use inth_oauth2::provider::google::Installed;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let client = Client::<Google>::new(
|
let client = Client::<Installed>::new(
|
||||||
String::from("143225766783-ip2d9qv6sdr37276t77luk6f7bhd6bj5.apps.googleusercontent.com"),
|
String::from("143225766783-ip2d9qv6sdr37276t77luk6f7bhd6bj5.apps.googleusercontent.com"),
|
||||||
String::from("3kZ5WomzHFlN2f_XbhkyPd3o"),
|
String::from("3kZ5WomzHFlN2f_XbhkyPd3o"),
|
||||||
Some(String::from("urn:ietf:wg:oauth:2.0:oob"))
|
Some(String::from("urn:ietf:wg:oauth:2.0:oob"))
|
|
@ -0,0 +1,24 @@
|
||||||
|
extern crate inth_oauth2;
|
||||||
|
|
||||||
|
use std::io;
|
||||||
|
|
||||||
|
use inth_oauth2::Client;
|
||||||
|
use inth_oauth2::provider::google::Web;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let client = Client::<Web>::new(
|
||||||
|
String::from("143225766783-0h4h5ktpvhc7kqp6ohbpd2sssqrap57n.apps.googleusercontent.com"),
|
||||||
|
String::from("7Xjn-vRN-8qsz3Zh9zZGkHsM"),
|
||||||
|
Some(String::from("https://cmcenroe.me/oauth2-paste/")),
|
||||||
|
);
|
||||||
|
|
||||||
|
let auth_uri = client.auth_uri(Some("https://www.googleapis.com/auth/userinfo.email"), None)
|
||||||
|
.unwrap();
|
||||||
|
println!("{}", auth_uri);
|
||||||
|
|
||||||
|
let mut code = String::new();
|
||||||
|
io::stdin().read_line(&mut code).unwrap();
|
||||||
|
|
||||||
|
let token = client.request_token(&Default::default(), code.trim()).unwrap();
|
||||||
|
println!("{:?}", token);
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ use url::{form_urlencoded, Url};
|
||||||
|
|
||||||
use error::OAuth2Error;
|
use error::OAuth2Error;
|
||||||
use provider::Provider;
|
use provider::Provider;
|
||||||
use token::{Token, Lifetime, Expiring};
|
use token::{Token, Lifetime, Refresh};
|
||||||
|
|
||||||
use self::response::FromResponse;
|
use self::response::FromResponse;
|
||||||
pub mod response;
|
pub mod response;
|
||||||
|
@ -38,9 +38,9 @@ impl<P: Provider> Client<P> {
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use inth_oauth2::Client;
|
/// use inth_oauth2::Client;
|
||||||
/// use inth_oauth2::provider::Google;
|
/// use inth_oauth2::provider::google::Installed;
|
||||||
///
|
///
|
||||||
/// let client = Client::<Google>::new(
|
/// let client = Client::<Installed>::new(
|
||||||
/// String::from("CLIENT_ID"),
|
/// String::from("CLIENT_ID"),
|
||||||
/// String::from("CLIENT_SECRET"),
|
/// String::from("CLIENT_SECRET"),
|
||||||
/// Some(String::from("urn:ietf:wg:oauth:2.0:oob"))
|
/// Some(String::from("urn:ietf:wg:oauth:2.0:oob"))
|
||||||
|
@ -63,9 +63,9 @@ impl<P: Provider> Client<P> {
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use inth_oauth2::Client;
|
/// use inth_oauth2::Client;
|
||||||
/// use inth_oauth2::provider::Google;
|
/// use inth_oauth2::provider::google::Installed;
|
||||||
///
|
///
|
||||||
/// let client = Client::<Google>::new(
|
/// let client = Client::<Installed>::new(
|
||||||
/// String::from("CLIENT_ID"),
|
/// String::from("CLIENT_ID"),
|
||||||
/// String::from("CLIENT_SECRET"),
|
/// String::from("CLIENT_SECRET"),
|
||||||
/// Some(String::from("urn:ietf:wg:oauth:2.0:oob"))
|
/// Some(String::from("urn:ietf:wg:oauth:2.0:oob"))
|
||||||
|
@ -157,7 +157,7 @@ impl<P: Provider> Client<P> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<P: Provider> Client<P> where P::Token: Token<Expiring> {
|
impl<P: Provider> Client<P> where P::Token: Token<Refresh> {
|
||||||
/// Refreshes an access token.
|
/// Refreshes an access token.
|
||||||
///
|
///
|
||||||
/// See [RFC 6749, section 6](http://tools.ietf.org/html/rfc6749#section-6).
|
/// See [RFC 6749, section 6](http://tools.ietf.org/html/rfc6749#section-6).
|
||||||
|
|
34
src/lib.rs
34
src/lib.rs
|
@ -14,6 +14,8 @@
|
||||||
//! Support for the following OAuth 2.0 providers is included:
|
//! Support for the following OAuth 2.0 providers is included:
|
||||||
//!
|
//!
|
||||||
//! - Google
|
//! - Google
|
||||||
|
//! - Web
|
||||||
|
//! - Installed
|
||||||
//! - GitHub
|
//! - GitHub
|
||||||
//! - Imgur
|
//! - Imgur
|
||||||
//!
|
//!
|
||||||
|
@ -30,9 +32,9 @@
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! use inth_oauth2::Client;
|
//! use inth_oauth2::Client;
|
||||||
//! use inth_oauth2::provider::Google;
|
//! use inth_oauth2::provider::google::Installed;
|
||||||
//!
|
//!
|
||||||
//! let client = Client::<Google>::new(
|
//! let client = Client::<Installed>::new(
|
||||||
//! String::from("client_id"),
|
//! String::from("client_id"),
|
||||||
//! String::from("client_secret"),
|
//! String::from("client_secret"),
|
||||||
//! Some(String::from("redirect_uri"))
|
//! Some(String::from("redirect_uri"))
|
||||||
|
@ -43,8 +45,8 @@
|
||||||
//!
|
//!
|
||||||
//! ```
|
//! ```
|
||||||
//! # use inth_oauth2::Client;
|
//! # use inth_oauth2::Client;
|
||||||
//! # use inth_oauth2::provider::Google;
|
//! # use inth_oauth2::provider::google::Installed;
|
||||||
//! # let client = Client::<Google>::new(String::new(), String::new(), None);
|
//! # let client = Client::<Installed>::new(String::new(), String::new(), None);
|
||||||
//! let auth_uri = client.auth_uri(Some("scope"), Some("state")).unwrap();
|
//! let auth_uri = client.auth_uri(Some("scope"), Some("state")).unwrap();
|
||||||
//! println!("Authorize the application by clicking on the link: {}", auth_uri);
|
//! println!("Authorize the application by clicking on the link: {}", auth_uri);
|
||||||
//! ```
|
//! ```
|
||||||
|
@ -54,8 +56,8 @@
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! use std::io;
|
//! use std::io;
|
||||||
//! use inth_oauth2::{Client, Token};
|
//! use inth_oauth2::{Client, Token};
|
||||||
//! # use inth_oauth2::provider::Google;
|
//! # use inth_oauth2::provider::google::Installed;
|
||||||
//! # let client = Client::<Google>::new(String::new(), String::new(), None);
|
//! # let client = Client::<Installed>::new(String::new(), String::new(), None);
|
||||||
//!
|
//!
|
||||||
//! let mut code = String::new();
|
//! let mut code = String::new();
|
||||||
//! io::stdin().read_line(&mut code).unwrap();
|
//! io::stdin().read_line(&mut code).unwrap();
|
||||||
|
@ -69,8 +71,8 @@
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! # use inth_oauth2::Client;
|
//! # use inth_oauth2::Client;
|
||||||
//! # use inth_oauth2::provider::Google;
|
//! # use inth_oauth2::provider::google::Installed;
|
||||||
//! # let client = Client::<Google>::new(String::new(), String::new(), None);
|
//! # let client = Client::<Installed>::new(String::new(), String::new(), None);
|
||||||
//! # let http_client = Default::default();
|
//! # let http_client = Default::default();
|
||||||
//! # let token = client.request_token(&http_client, "").unwrap();
|
//! # let token = client.request_token(&http_client, "").unwrap();
|
||||||
//! let token = client.refresh_token(&http_client, token, None).unwrap();
|
//! let token = client.refresh_token(&http_client, token, None).unwrap();
|
||||||
|
@ -80,8 +82,8 @@
|
||||||
//!
|
//!
|
||||||
//! ```no_run
|
//! ```no_run
|
||||||
//! # use inth_oauth2::Client;
|
//! # use inth_oauth2::Client;
|
||||||
//! # use inth_oauth2::provider::Google;
|
//! # use inth_oauth2::provider::google::Installed;
|
||||||
//! # let client = Client::<Google>::new(String::new(), String::new(), None);
|
//! # let client = Client::<Installed>::new(String::new(), String::new(), None);
|
||||||
//! # let http_client = Default::default();
|
//! # let http_client = Default::default();
|
||||||
//! # let mut token = client.request_token(&http_client, "").unwrap();
|
//! # let mut token = client.request_token(&http_client, "").unwrap();
|
||||||
//! // Refresh token only if it has expired.
|
//! // Refresh token only if it has expired.
|
||||||
|
@ -96,12 +98,12 @@
|
||||||
//! # extern crate hyper;
|
//! # extern crate hyper;
|
||||||
//! # extern crate inth_oauth2;
|
//! # extern crate inth_oauth2;
|
||||||
//! # use inth_oauth2::Client;
|
//! # use inth_oauth2::Client;
|
||||||
//! # use inth_oauth2::provider::Google;
|
//! # use inth_oauth2::provider::google::Installed;
|
||||||
//! use hyper::header::Authorization;
|
//! use hyper::header::Authorization;
|
||||||
//!
|
//!
|
||||||
//! # fn main() {
|
//! # fn main() {
|
||||||
//! let client = hyper::Client::new();
|
//! let client = hyper::Client::new();
|
||||||
//! # let oauth_client = Client::<Google>::new(String::new(), String::new(), None);
|
//! # let oauth_client = Client::<Installed>::new(String::new(), String::new(), None);
|
||||||
//! # let token = oauth_client.request_token(&client, "").unwrap();
|
//! # let token = oauth_client.request_token(&client, "").unwrap();
|
||||||
//! let request = client.get("https://example.com/resource")
|
//! let request = client.get("https://example.com/resource")
|
||||||
//! .header(Into::<Authorization<_>>::into(&token));
|
//! .header(Into::<Authorization<_>>::into(&token));
|
||||||
|
@ -117,11 +119,11 @@
|
||||||
//! # extern crate inth_oauth2;
|
//! # extern crate inth_oauth2;
|
||||||
//! # extern crate rustc_serialize;
|
//! # extern crate rustc_serialize;
|
||||||
//! # use inth_oauth2::Client;
|
//! # use inth_oauth2::Client;
|
||||||
//! # use inth_oauth2::provider::Google;
|
//! # use inth_oauth2::provider::google::Installed;
|
||||||
//! use rustc_serialize::json;
|
//! use rustc_serialize::json;
|
||||||
//! # fn main() {
|
//! # fn main() {
|
||||||
//! # let http_client = Default::default();
|
//! # let http_client = Default::default();
|
||||||
//! # let client = Client::<Google>::new(String::new(), String::new(), None);
|
//! # let client = Client::<Installed>::new(String::new(), String::new(), None);
|
||||||
//! # let token = client.request_token(&http_client, "").unwrap();
|
//! # let token = client.request_token(&http_client, "").unwrap();
|
||||||
//! let json = json::encode(&token).unwrap();
|
//! let json = json::encode(&token).unwrap();
|
||||||
//! # }
|
//! # }
|
||||||
|
@ -131,10 +133,10 @@
|
||||||
//! # extern crate inth_oauth2;
|
//! # extern crate inth_oauth2;
|
||||||
//! extern crate serde_json;
|
//! extern crate serde_json;
|
||||||
//! # use inth_oauth2::Client;
|
//! # use inth_oauth2::Client;
|
||||||
//! # use inth_oauth2::provider::Google;
|
//! # use inth_oauth2::provider::google::Installed;
|
||||||
//! # fn main() {
|
//! # fn main() {
|
||||||
//! # let http_client = Default::default();
|
//! # let http_client = Default::default();
|
||||||
//! # let client = Client::<Google>::new(String::new(), String::new(), None);
|
//! # let client = Client::<Installed>::new(String::new(), String::new(), None);
|
||||||
//! # let token = client.request_token(&http_client, "").unwrap();
|
//! # let token = client.request_token(&http_client, "").unwrap();
|
||||||
//! let json = serde_json::to_string(&token).unwrap();
|
//! let json = serde_json::to_string(&token).unwrap();
|
||||||
//! # }
|
//! # }
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//! Providers.
|
//! Providers.
|
||||||
|
|
||||||
use token::{Token, Lifetime, Bearer, Static, Expiring};
|
use token::{Token, Lifetime, Bearer, Static, Refresh};
|
||||||
|
|
||||||
/// OAuth 2.0 providers.
|
/// OAuth 2.0 providers.
|
||||||
pub trait Provider {
|
pub trait Provider {
|
||||||
|
@ -35,17 +35,39 @@ pub trait Provider {
|
||||||
fn credentials_in_body() -> bool { false }
|
fn credentials_in_body() -> bool { false }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Google OAuth 2.0 provider.
|
/// Google OAuth 2.0 providers.
|
||||||
///
|
///
|
||||||
/// See [Using OAuth 2.0 to Access Google
|
/// See [Using OAuth 2.0 to Access Google
|
||||||
/// APIs](https://developers.google.com/identity/protocols/OAuth2).
|
/// APIs](https://developers.google.com/identity/protocols/OAuth2).
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
pub mod google {
|
||||||
pub struct Google;
|
use token::{Bearer, Expiring, Refresh};
|
||||||
impl Provider for Google {
|
use super::Provider;
|
||||||
type Lifetime = Expiring;
|
|
||||||
type Token = Bearer<Expiring>;
|
/// Google OAuth 2.0 provider for web applications.
|
||||||
fn auth_uri() -> &'static str { "https://accounts.google.com/o/oauth2/v2/auth" }
|
///
|
||||||
fn token_uri() -> &'static str { "https://www.googleapis.com/oauth2/v4/token" }
|
/// See [Using OAuth 2.0 for Web Server
|
||||||
|
/// Applications](https://developers.google.com/identity/protocols/OAuth2WebServer).
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct Web;
|
||||||
|
impl Provider for Web {
|
||||||
|
type Lifetime = Expiring;
|
||||||
|
type Token = Bearer<Expiring>;
|
||||||
|
fn auth_uri() -> &'static str { "https://accounts.google.com/o/oauth2/v2/auth" }
|
||||||
|
fn token_uri() -> &'static str { "https://www.googleapis.com/oauth2/v4/token" }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Google OAuth 2.0 provider for installed applications.
|
||||||
|
///
|
||||||
|
/// See [Using OAuth 2.0 for Installed
|
||||||
|
/// Applications](https://developers.google.com/identity/protocols/OAuth2InstalledApp).
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct Installed;
|
||||||
|
impl Provider for Installed {
|
||||||
|
type Lifetime = Refresh;
|
||||||
|
type Token = Bearer<Refresh>;
|
||||||
|
fn auth_uri() -> &'static str { "https://accounts.google.com/o/oauth2/v2/auth" }
|
||||||
|
fn token_uri() -> &'static str { "https://www.googleapis.com/oauth2/v4/token" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// GitHub OAuth 2.0 provider.
|
/// GitHub OAuth 2.0 provider.
|
||||||
|
@ -66,8 +88,8 @@ impl Provider for GitHub {
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct Imgur;
|
pub struct Imgur;
|
||||||
impl Provider for Imgur {
|
impl Provider for Imgur {
|
||||||
type Lifetime = Expiring;
|
type Lifetime = Refresh;
|
||||||
type Token = Bearer<Expiring>;
|
type Token = Bearer<Refresh>;
|
||||||
fn auth_uri() -> &'static str { "https://api.imgur.com/oauth2/authorize" }
|
fn auth_uri() -> &'static str { "https://api.imgur.com/oauth2/authorize" }
|
||||||
fn token_uri() -> &'static str { "https://api.imgur.com/oauth2/token" }
|
fn token_uri() -> &'static str { "https://api.imgur.com/oauth2/token" }
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,7 +160,7 @@ mod tests {
|
||||||
use serde_json;
|
use serde_json;
|
||||||
|
|
||||||
use client::response::{FromResponse, ParseError};
|
use client::response::{FromResponse, ParseError};
|
||||||
use token::{Static, Expiring};
|
use token::{Static, Refresh};
|
||||||
use super::Bearer;
|
use super::Bearer;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -214,7 +214,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_response_expiring() {
|
fn from_response_refresh() {
|
||||||
let json = Json::from_str(r#"
|
let json = Json::from_str(r#"
|
||||||
{
|
{
|
||||||
"token_type":"Bearer",
|
"token_type":"Bearer",
|
||||||
|
@ -223,17 +223,17 @@ mod tests {
|
||||||
"refresh_token":"bbbbbbbb"
|
"refresh_token":"bbbbbbbb"
|
||||||
}
|
}
|
||||||
"#).unwrap();
|
"#).unwrap();
|
||||||
let bearer = Bearer::<Expiring>::from_response(&json).unwrap();
|
let bearer = Bearer::<Refresh>::from_response(&json).unwrap();
|
||||||
assert_eq!("aaaaaaaa", bearer.access_token);
|
assert_eq!("aaaaaaaa", bearer.access_token);
|
||||||
assert_eq!(None, bearer.scope);
|
assert_eq!(None, bearer.scope);
|
||||||
let expiring = bearer.lifetime;
|
let refresh = bearer.lifetime;
|
||||||
assert_eq!("bbbbbbbb", expiring.refresh_token());
|
assert_eq!("bbbbbbbb", refresh.refresh_token());
|
||||||
assert!(expiring.expires() > &UTC::now());
|
assert!(refresh.expires() > &UTC::now());
|
||||||
assert!(expiring.expires() <= &(UTC::now() + Duration::seconds(3600)));
|
assert!(refresh.expires() <= &(UTC::now() + Duration::seconds(3600)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_response_inherit_expiring() {
|
fn from_response_inherit_refresh() {
|
||||||
let json = Json::from_str(r#"
|
let json = Json::from_str(r#"
|
||||||
{
|
{
|
||||||
"token_type":"Bearer",
|
"token_type":"Bearer",
|
||||||
|
@ -242,7 +242,7 @@ mod tests {
|
||||||
"refresh_token":"bbbbbbbb"
|
"refresh_token":"bbbbbbbb"
|
||||||
}
|
}
|
||||||
"#).unwrap();
|
"#).unwrap();
|
||||||
let prev = Bearer::<Expiring>::from_response(&json).unwrap();
|
let prev = Bearer::<Refresh>::from_response(&json).unwrap();
|
||||||
|
|
||||||
let json = Json::from_str(r#"
|
let json = Json::from_str(r#"
|
||||||
{
|
{
|
||||||
|
@ -251,13 +251,13 @@ mod tests {
|
||||||
"expires_in":3600
|
"expires_in":3600
|
||||||
}
|
}
|
||||||
"#).unwrap();
|
"#).unwrap();
|
||||||
let bearer = Bearer::<Expiring>::from_response_inherit(&json, &prev).unwrap();
|
let bearer = Bearer::<Refresh>::from_response_inherit(&json, &prev).unwrap();
|
||||||
assert_eq!("cccccccc", bearer.access_token);
|
assert_eq!("cccccccc", bearer.access_token);
|
||||||
assert_eq!(None, bearer.scope);
|
assert_eq!(None, bearer.scope);
|
||||||
let expiring = bearer.lifetime;
|
let refresh = bearer.lifetime;
|
||||||
assert_eq!("bbbbbbbb", expiring.refresh_token());
|
assert_eq!("bbbbbbbb", refresh.refresh_token());
|
||||||
assert!(expiring.expires() > &UTC::now());
|
assert!(refresh.expires() > &UTC::now());
|
||||||
assert!(expiring.expires() <= &(UTC::now() + Duration::seconds(3600)));
|
assert!(refresh.expires() <= &(UTC::now() + Duration::seconds(3600)));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -8,18 +8,12 @@ use super::Lifetime;
|
||||||
use client::response::{FromResponse, ParseError, JsonHelper};
|
use client::response::{FromResponse, ParseError, JsonHelper};
|
||||||
|
|
||||||
/// An expiring token.
|
/// An expiring token.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
pub struct Expiring {
|
pub struct Expiring {
|
||||||
refresh_token: String,
|
|
||||||
expires: DateTime<UTC>,
|
expires: DateTime<UTC>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Expiring {
|
impl Expiring {
|
||||||
/// Returns the refresh token.
|
|
||||||
///
|
|
||||||
/// See [RFC 6749, section 1.5](http://tools.ietf.org/html/rfc6749#section-1.5).
|
|
||||||
pub fn refresh_token(&self) -> &str { &self.refresh_token }
|
|
||||||
|
|
||||||
/// Returns the expiry time of the access token.
|
/// Returns the expiry time of the access token.
|
||||||
pub fn expires(&self) -> &DateTime<UTC> { &self.expires }
|
pub fn expires(&self) -> &DateTime<UTC> { &self.expires }
|
||||||
}
|
}
|
||||||
|
@ -32,26 +26,13 @@ impl FromResponse for Expiring {
|
||||||
fn from_response(json: &Json) -> Result<Self, ParseError> {
|
fn from_response(json: &Json) -> Result<Self, ParseError> {
|
||||||
let obj = try!(JsonHelper(json).as_object());
|
let obj = try!(JsonHelper(json).as_object());
|
||||||
|
|
||||||
let refresh_token = try!(obj.get_string("refresh_token"));
|
if obj.0.contains_key("refresh_token") {
|
||||||
|
return Err(ParseError::UnexpectedField("refresh_token"));
|
||||||
|
}
|
||||||
|
|
||||||
let expires_in = try!(obj.get_i64("expires_in"));
|
let expires_in = try!(obj.get_i64("expires_in"));
|
||||||
|
|
||||||
Ok(Expiring {
|
Ok(Expiring {
|
||||||
refresh_token: refresh_token.into(),
|
|
||||||
expires: UTC::now() + Duration::seconds(expires_in),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn from_response_inherit(json: &Json, prev: &Self) -> Result<Self, ParseError> {
|
|
||||||
let obj = try!(JsonHelper(json).as_object());
|
|
||||||
|
|
||||||
let refresh_token = try! {
|
|
||||||
obj.get_string("refresh_token")
|
|
||||||
.or(Ok(&prev.refresh_token))
|
|
||||||
};
|
|
||||||
let expires_in = try!(obj.get_i64("expires_in"));
|
|
||||||
|
|
||||||
Ok(Expiring {
|
|
||||||
refresh_token: refresh_token.into(),
|
|
||||||
expires: UTC::now() + Duration::seconds(expires_in),
|
expires: UTC::now() + Duration::seconds(expires_in),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -59,14 +40,12 @@ impl FromResponse for Expiring {
|
||||||
|
|
||||||
#[derive(RustcEncodable, RustcDecodable)]
|
#[derive(RustcEncodable, RustcDecodable)]
|
||||||
struct Serializable {
|
struct Serializable {
|
||||||
refresh_token: String,
|
|
||||||
expires: i64,
|
expires: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> From<&'a Expiring> for Serializable {
|
impl<'a> From<&'a Expiring> for Serializable {
|
||||||
fn from(expiring: &Expiring) -> Self {
|
fn from(expiring: &Expiring) -> Self {
|
||||||
Serializable {
|
Serializable {
|
||||||
refresh_token: expiring.refresh_token.clone(),
|
|
||||||
expires: expiring.expires.timestamp(),
|
expires: expiring.expires.timestamp(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,7 +54,6 @@ impl<'a> From<&'a Expiring> for Serializable {
|
||||||
impl Into<Expiring> for Serializable {
|
impl Into<Expiring> for Serializable {
|
||||||
fn into(self) -> Expiring {
|
fn into(self) -> Expiring {
|
||||||
Expiring {
|
Expiring {
|
||||||
refresh_token: self.refresh_token,
|
|
||||||
expires: UTC.timestamp(self.expires, 0),
|
expires: UTC.timestamp(self.expires, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -104,18 +82,17 @@ impl<'a> ser::MapVisitor for SerVisitor<'a> {
|
||||||
fn visit<S: Serializer>(&mut self, serializer: &mut S) -> Result<Option<()>, S::Error> {
|
fn visit<S: Serializer>(&mut self, serializer: &mut S) -> Result<Option<()>, S::Error> {
|
||||||
self.1 += 1;
|
self.1 += 1;
|
||||||
match self.1 {
|
match self.1 {
|
||||||
1 => serializer.serialize_struct_elt("refresh_token", &self.0.refresh_token).map(Some),
|
1 => serializer.serialize_struct_elt("expires", &self.0.expires.timestamp()).map(Some),
|
||||||
2 => serializer.serialize_struct_elt("expires", &self.0.expires.timestamp()).map(Some),
|
|
||||||
_ => Ok(None),
|
_ => Ok(None),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn len(&self) -> Option<usize> { Some(2) }
|
fn len(&self) -> Option<usize> { Some(1) }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Deserialize for Expiring {
|
impl Deserialize for Expiring {
|
||||||
fn deserialize<D: Deserializer>(deserializer: &mut D) -> Result<Self, D::Error> {
|
fn deserialize<D: Deserializer>(deserializer: &mut D) -> Result<Self, D::Error> {
|
||||||
static FIELDS: &'static [&'static str] = &["refresh_token", "expires"];
|
static FIELDS: &'static [&'static str] = &["expires"];
|
||||||
deserializer.deserialize_struct("Expiring", FIELDS, DeVisitor)
|
deserializer.deserialize_struct("Expiring", FIELDS, DeVisitor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,21 +102,15 @@ impl de::Visitor for DeVisitor {
|
||||||
type Value = Expiring;
|
type Value = Expiring;
|
||||||
|
|
||||||
fn visit_map<V: de::MapVisitor>(&mut self, mut visitor: V) -> Result<Expiring, V::Error> {
|
fn visit_map<V: de::MapVisitor>(&mut self, mut visitor: V) -> Result<Expiring, V::Error> {
|
||||||
let mut refresh_token = None;
|
|
||||||
let mut expires = None;
|
let mut expires = None;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match try!(visitor.visit_key()) {
|
match try!(visitor.visit_key()) {
|
||||||
Some(Field::RefreshToken) => refresh_token = Some(try!(visitor.visit_value())),
|
|
||||||
Some(Field::Expires) => expires = Some(try!(visitor.visit_value())),
|
Some(Field::Expires) => expires = Some(try!(visitor.visit_value())),
|
||||||
None => break,
|
None => break,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let refresh_token = match refresh_token {
|
|
||||||
Some(s) => s,
|
|
||||||
None => return visitor.missing_field("refresh_token"),
|
|
||||||
};
|
|
||||||
let expires = match expires {
|
let expires = match expires {
|
||||||
Some(i) => UTC.timestamp(i, 0),
|
Some(i) => UTC.timestamp(i, 0),
|
||||||
None => return visitor.missing_field("expires"),
|
None => return visitor.missing_field("expires"),
|
||||||
|
@ -148,14 +119,12 @@ impl de::Visitor for DeVisitor {
|
||||||
try!(visitor.end());
|
try!(visitor.end());
|
||||||
|
|
||||||
Ok(Expiring {
|
Ok(Expiring {
|
||||||
refresh_token: refresh_token,
|
|
||||||
expires: expires,
|
expires: expires,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Field {
|
enum Field {
|
||||||
RefreshToken,
|
|
||||||
Expires,
|
Expires,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -171,9 +140,8 @@ impl de::Visitor for FieldVisitor {
|
||||||
|
|
||||||
fn visit_str<E: de::Error>(&mut self, value: &str) -> Result<Field, E> {
|
fn visit_str<E: de::Error>(&mut self, value: &str) -> Result<Field, E> {
|
||||||
match value {
|
match value {
|
||||||
"refresh_token" => Ok(Field::RefreshToken),
|
|
||||||
"expires" => Ok(Field::Expires),
|
"expires" => Ok(Field::Expires),
|
||||||
_ => Err(de::Error::custom("expected refresh_token or expires")),
|
_ => Err(de::Error::custom("expected expires")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -189,22 +157,8 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_response() {
|
fn from_response() {
|
||||||
let json = Json::from_str(r#"{"refresh_token":"aaaaaaaa","expires_in":3600}"#).unwrap();
|
|
||||||
let expiring = Expiring::from_response(&json).unwrap();
|
|
||||||
assert_eq!("aaaaaaaa", expiring.refresh_token);
|
|
||||||
assert!(expiring.expires > UTC::now());
|
|
||||||
assert!(expiring.expires <= UTC::now() + Duration::seconds(3600));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn from_response_inherit() {
|
|
||||||
let json = Json::from_str(r#"{"expires_in":3600}"#).unwrap();
|
let json = Json::from_str(r#"{"expires_in":3600}"#).unwrap();
|
||||||
let prev = Expiring {
|
let expiring = Expiring::from_response(&json).unwrap();
|
||||||
refresh_token: String::from("aaaaaaaa"),
|
|
||||||
expires: UTC::now(),
|
|
||||||
};
|
|
||||||
let expiring = Expiring::from_response_inherit(&json, &prev).unwrap();
|
|
||||||
assert_eq!("aaaaaaaa", expiring.refresh_token);
|
|
||||||
assert!(expiring.expires > UTC::now());
|
assert!(expiring.expires > UTC::now());
|
||||||
assert!(expiring.expires <= UTC::now() + Duration::seconds(3600));
|
assert!(expiring.expires <= UTC::now() + Duration::seconds(3600));
|
||||||
}
|
}
|
||||||
|
@ -212,7 +166,6 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn encode_decode() {
|
fn encode_decode() {
|
||||||
let expiring = Expiring {
|
let expiring = Expiring {
|
||||||
refresh_token: String::from("foo"),
|
|
||||||
expires: UTC::now().with_nanosecond(0).unwrap(),
|
expires: UTC::now().with_nanosecond(0).unwrap(),
|
||||||
};
|
};
|
||||||
let json = json::encode(&expiring).unwrap();
|
let json = json::encode(&expiring).unwrap();
|
||||||
|
@ -223,7 +176,6 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn serialize_deserialize() {
|
fn serialize_deserialize() {
|
||||||
let original = Expiring {
|
let original = Expiring {
|
||||||
refresh_token: String::from("foo"),
|
|
||||||
expires: UTC::now().with_nanosecond(0).unwrap(),
|
expires: UTC::now().with_nanosecond(0).unwrap(),
|
||||||
};
|
};
|
||||||
let serialized = serde_json::to_value(&original);
|
let serialized = serde_json::to_value(&original);
|
||||||
|
|
|
@ -37,3 +37,6 @@ mod statik;
|
||||||
|
|
||||||
pub use self::expiring::Expiring;
|
pub use self::expiring::Expiring;
|
||||||
mod expiring;
|
mod expiring;
|
||||||
|
|
||||||
|
pub use self::refresh::Refresh;
|
||||||
|
mod refresh;
|
||||||
|
|
|
@ -0,0 +1,233 @@
|
||||||
|
use chrono::{DateTime, UTC, Duration, TimeZone};
|
||||||
|
use rustc_serialize::json::Json;
|
||||||
|
use rustc_serialize::{Encodable, Encoder, Decodable, Decoder};
|
||||||
|
use serde::{Serialize, Serializer, Deserialize, Deserializer};
|
||||||
|
use serde::{ser, de};
|
||||||
|
|
||||||
|
use super::Lifetime;
|
||||||
|
use client::response::{FromResponse, ParseError, JsonHelper};
|
||||||
|
|
||||||
|
/// An expiring token which can be refreshed.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct Refresh {
|
||||||
|
refresh_token: String,
|
||||||
|
expires: DateTime<UTC>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Refresh {
|
||||||
|
/// Returns the refresh token.
|
||||||
|
///
|
||||||
|
/// See [RFC 6749, section 1.5](http://tools.ietf.org/html/rfc6749#section-1.5).
|
||||||
|
pub fn refresh_token(&self) -> &str { &self.refresh_token }
|
||||||
|
|
||||||
|
/// Returns the expiry time of the access token.
|
||||||
|
pub fn expires(&self) -> &DateTime<UTC> { &self.expires }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Lifetime for Refresh {
|
||||||
|
fn expired(&self) -> bool { self.expires < UTC::now() }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromResponse for Refresh {
|
||||||
|
fn from_response(json: &Json) -> Result<Self, ParseError> {
|
||||||
|
let obj = try!(JsonHelper(json).as_object());
|
||||||
|
|
||||||
|
let refresh_token = try!(obj.get_string("refresh_token"));
|
||||||
|
let expires_in = try!(obj.get_i64("expires_in"));
|
||||||
|
|
||||||
|
Ok(Refresh {
|
||||||
|
refresh_token: refresh_token.into(),
|
||||||
|
expires: UTC::now() + Duration::seconds(expires_in),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn from_response_inherit(json: &Json, prev: &Self) -> Result<Self, ParseError> {
|
||||||
|
let obj = try!(JsonHelper(json).as_object());
|
||||||
|
|
||||||
|
let refresh_token = try! {
|
||||||
|
obj.get_string("refresh_token")
|
||||||
|
.or(Ok(&prev.refresh_token))
|
||||||
|
};
|
||||||
|
let expires_in = try!(obj.get_i64("expires_in"));
|
||||||
|
|
||||||
|
Ok(Refresh {
|
||||||
|
refresh_token: refresh_token.into(),
|
||||||
|
expires: UTC::now() + Duration::seconds(expires_in),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(RustcEncodable, RustcDecodable)]
|
||||||
|
struct Serializable {
|
||||||
|
refresh_token: String,
|
||||||
|
expires: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a Refresh> for Serializable {
|
||||||
|
fn from(refresh: &Refresh) -> Self {
|
||||||
|
Serializable {
|
||||||
|
refresh_token: refresh.refresh_token.clone(),
|
||||||
|
expires: refresh.expires.timestamp(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<Refresh> for Serializable {
|
||||||
|
fn into(self) -> Refresh {
|
||||||
|
Refresh {
|
||||||
|
refresh_token: self.refresh_token,
|
||||||
|
expires: UTC.timestamp(self.expires, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encodable for Refresh {
|
||||||
|
fn encode<S: Encoder>(&self, s: &mut S) -> Result<(), S::Error> {
|
||||||
|
Serializable::from(self).encode(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decodable for Refresh {
|
||||||
|
fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
|
||||||
|
Serializable::decode(d).map(Into::into)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for Refresh {
|
||||||
|
fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
|
||||||
|
serializer.serialize_struct("Refresh", SerVisitor(self, 0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SerVisitor<'a>(&'a Refresh, u8);
|
||||||
|
impl<'a> ser::MapVisitor for SerVisitor<'a> {
|
||||||
|
fn visit<S: Serializer>(&mut self, serializer: &mut S) -> Result<Option<()>, S::Error> {
|
||||||
|
self.1 += 1;
|
||||||
|
match self.1 {
|
||||||
|
1 => serializer.serialize_struct_elt("refresh_token", &self.0.refresh_token).map(Some),
|
||||||
|
2 => serializer.serialize_struct_elt("expires", &self.0.expires.timestamp()).map(Some),
|
||||||
|
_ => Ok(None),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> Option<usize> { Some(2) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deserialize for Refresh {
|
||||||
|
fn deserialize<D: Deserializer>(deserializer: &mut D) -> Result<Self, D::Error> {
|
||||||
|
static FIELDS: &'static [&'static str] = &["refresh_token", "expires"];
|
||||||
|
deserializer.deserialize_struct("Refresh", FIELDS, DeVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DeVisitor;
|
||||||
|
impl de::Visitor for DeVisitor {
|
||||||
|
type Value = Refresh;
|
||||||
|
|
||||||
|
fn visit_map<V: de::MapVisitor>(&mut self, mut visitor: V) -> Result<Refresh, V::Error> {
|
||||||
|
let mut refresh_token = None;
|
||||||
|
let mut expires = None;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match try!(visitor.visit_key()) {
|
||||||
|
Some(Field::RefreshToken) => refresh_token = Some(try!(visitor.visit_value())),
|
||||||
|
Some(Field::Expires) => expires = Some(try!(visitor.visit_value())),
|
||||||
|
None => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let refresh_token = match refresh_token {
|
||||||
|
Some(s) => s,
|
||||||
|
None => return visitor.missing_field("refresh_token"),
|
||||||
|
};
|
||||||
|
let expires = match expires {
|
||||||
|
Some(i) => UTC.timestamp(i, 0),
|
||||||
|
None => return visitor.missing_field("expires"),
|
||||||
|
};
|
||||||
|
|
||||||
|
try!(visitor.end());
|
||||||
|
|
||||||
|
Ok(Refresh {
|
||||||
|
refresh_token: refresh_token,
|
||||||
|
expires: expires,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Field {
|
||||||
|
RefreshToken,
|
||||||
|
Expires,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deserialize for Field {
|
||||||
|
fn deserialize<D: Deserializer>(deserializer: &mut D) -> Result<Self, D::Error> {
|
||||||
|
deserializer.deserialize(FieldVisitor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct FieldVisitor;
|
||||||
|
impl de::Visitor for FieldVisitor {
|
||||||
|
type Value = Field;
|
||||||
|
|
||||||
|
fn visit_str<E: de::Error>(&mut self, value: &str) -> Result<Field, E> {
|
||||||
|
match value {
|
||||||
|
"refresh_token" => Ok(Field::RefreshToken),
|
||||||
|
"expires" => Ok(Field::Expires),
|
||||||
|
_ => Err(de::Error::custom("expected refresh_token or expires")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use chrono::{UTC, Duration, Timelike};
|
||||||
|
use rustc_serialize::json::{self, Json};
|
||||||
|
use serde_json;
|
||||||
|
|
||||||
|
use client::response::FromResponse;
|
||||||
|
use super::Refresh;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_response() {
|
||||||
|
let json = Json::from_str(r#"{"refresh_token":"aaaaaaaa","expires_in":3600}"#).unwrap();
|
||||||
|
let refresh = Refresh::from_response(&json).unwrap();
|
||||||
|
assert_eq!("aaaaaaaa", refresh.refresh_token);
|
||||||
|
assert!(refresh.expires > UTC::now());
|
||||||
|
assert!(refresh.expires <= UTC::now() + Duration::seconds(3600));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_response_inherit() {
|
||||||
|
let json = Json::from_str(r#"{"expires_in":3600}"#).unwrap();
|
||||||
|
let prev = Refresh {
|
||||||
|
refresh_token: String::from("aaaaaaaa"),
|
||||||
|
expires: UTC::now(),
|
||||||
|
};
|
||||||
|
let refresh = Refresh::from_response_inherit(&json, &prev).unwrap();
|
||||||
|
assert_eq!("aaaaaaaa", refresh.refresh_token);
|
||||||
|
assert!(refresh.expires > UTC::now());
|
||||||
|
assert!(refresh.expires <= UTC::now() + Duration::seconds(3600));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn encode_decode() {
|
||||||
|
let refresh = Refresh {
|
||||||
|
refresh_token: String::from("foo"),
|
||||||
|
expires: UTC::now().with_nanosecond(0).unwrap(),
|
||||||
|
};
|
||||||
|
let json = json::encode(&refresh).unwrap();
|
||||||
|
let decoded = json::decode(&json).unwrap();
|
||||||
|
assert_eq!(refresh, decoded);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn serialize_deserialize() {
|
||||||
|
let original = Refresh {
|
||||||
|
refresh_token: String::from("foo"),
|
||||||
|
expires: UTC::now().with_nanosecond(0).unwrap(),
|
||||||
|
};
|
||||||
|
let serialized = serde_json::to_value(&original);
|
||||||
|
let deserialized = serde_json::from_value(serialized).unwrap();
|
||||||
|
assert_eq!(original, deserialized);
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,8 +11,22 @@ fn assert_get_uri_ok(uri: &str) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn google_auth_uri_ok() {
|
fn google_web_auth_uri_ok() {
|
||||||
let client = Client::<Google>::new(
|
let client = Client::<google::Web>::new(
|
||||||
|
String::from("143225766783-0h4h5ktpvhc7kqp6ohbpd2sssqrap57n.apps.googleusercontent.com"),
|
||||||
|
String::new(),
|
||||||
|
Some(String::from("https://cmcenroe.me/oauth2-paste/")),
|
||||||
|
);
|
||||||
|
let auth_uri = client.auth_uri(
|
||||||
|
Some("https://www.googleapis.com/auth/userinfo.email"),
|
||||||
|
Some("state"),
|
||||||
|
).unwrap();
|
||||||
|
assert_get_uri_ok(&auth_uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn google_installed_auth_uri_ok() {
|
||||||
|
let client = Client::<google::Installed>::new(
|
||||||
String::from("143225766783-ip2d9qv6sdr37276t77luk6f7bhd6bj5.apps.googleusercontent.com"),
|
String::from("143225766783-ip2d9qv6sdr37276t77luk6f7bhd6bj5.apps.googleusercontent.com"),
|
||||||
String::new(),
|
String::new(),
|
||||||
Some(String::from("urn:ietf:wg:oauth:2.0:oob"))
|
Some(String::from("urn:ietf:wg:oauth:2.0:oob"))
|
||||||
|
|
|
@ -9,7 +9,7 @@ use inth_oauth2::{Client, ClientError, Token, Lifetime};
|
||||||
use inth_oauth2::error::OAuth2ErrorCode;
|
use inth_oauth2::error::OAuth2ErrorCode;
|
||||||
|
|
||||||
mod provider {
|
mod provider {
|
||||||
use inth_oauth2::token::{Bearer, Static, Expiring};
|
use inth_oauth2::token::{Bearer, Static, Expiring, Refresh};
|
||||||
use inth_oauth2::provider::Provider;
|
use inth_oauth2::provider::Provider;
|
||||||
|
|
||||||
pub struct BearerStatic;
|
pub struct BearerStatic;
|
||||||
|
@ -27,6 +27,14 @@ mod provider {
|
||||||
fn auth_uri() -> &'static str { "https://example.com/oauth/auth" }
|
fn auth_uri() -> &'static str { "https://example.com/oauth/auth" }
|
||||||
fn token_uri() -> &'static str { "https://example.com/oauth/token" }
|
fn token_uri() -> &'static str { "https://example.com/oauth/token" }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct BearerRefresh;
|
||||||
|
impl Provider for BearerRefresh {
|
||||||
|
type Lifetime = Refresh;
|
||||||
|
type Token = Bearer<Refresh>;
|
||||||
|
fn auth_uri() -> &'static str { "https://example.com/oauth/auth" }
|
||||||
|
fn token_uri() -> &'static str { "https://example.com/oauth/token" }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod connector {
|
mod connector {
|
||||||
|
@ -38,11 +46,15 @@ mod connector {
|
||||||
|
|
||||||
mock_connector_in_order!(BearerExpiring {
|
mock_connector_in_order!(BearerExpiring {
|
||||||
include_str!("response/request_token_bearer_expiring.http")
|
include_str!("response/request_token_bearer_expiring.http")
|
||||||
|
});
|
||||||
|
|
||||||
|
mock_connector_in_order!(BearerRefresh {
|
||||||
|
include_str!("response/request_token_bearer_refresh.http")
|
||||||
include_str!("response/refresh_token_bearer_full.http")
|
include_str!("response/refresh_token_bearer_full.http")
|
||||||
});
|
});
|
||||||
|
|
||||||
mock_connector_in_order!(BearerExpiringPartial {
|
mock_connector_in_order!(BearerRefreshPartial {
|
||||||
include_str!("response/request_token_bearer_expiring.http")
|
include_str!("response/request_token_bearer_refresh.http")
|
||||||
include_str!("response/refresh_token_bearer_partial.http")
|
include_str!("response/refresh_token_bearer_partial.http")
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -51,7 +63,7 @@ mod connector {
|
||||||
});
|
});
|
||||||
|
|
||||||
mock_connector_in_order!(RefreshInvalidRequest {
|
mock_connector_in_order!(RefreshInvalidRequest {
|
||||||
include_str!("response/request_token_bearer_expiring.http")
|
include_str!("response/request_token_bearer_refresh.http")
|
||||||
include_str!("response/invalid_request.http")
|
include_str!("response/invalid_request.http")
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -82,6 +94,17 @@ fn request_token_bearer_expiring_success() {
|
||||||
let token = client.request_token(&http_client, "code").unwrap();
|
let token = client.request_token(&http_client, "code").unwrap();
|
||||||
assert_eq!("aaaaaaaa", token.access_token());
|
assert_eq!("aaaaaaaa", token.access_token());
|
||||||
assert_eq!(Some("example"), token.scope());
|
assert_eq!(Some("example"), token.scope());
|
||||||
|
assert_eq!(false, token.lifetime().expired());
|
||||||
|
assert!(token.lifetime().expires() > &UTC::now());
|
||||||
|
assert!(token.lifetime().expires() <= &(UTC::now() + Duration::seconds(3600)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn request_token_bearer_refresh_success() {
|
||||||
|
let (client, http_client) = mock_client!(provider::BearerRefresh, connector::BearerRefresh);
|
||||||
|
let token = client.request_token(&http_client, "code").unwrap();
|
||||||
|
assert_eq!("aaaaaaaa", token.access_token());
|
||||||
|
assert_eq!(Some("example"), token.scope());
|
||||||
assert_eq!("bbbbbbbb", token.lifetime().refresh_token());
|
assert_eq!("bbbbbbbb", token.lifetime().refresh_token());
|
||||||
assert_eq!(false, token.lifetime().expired());
|
assert_eq!(false, token.lifetime().expired());
|
||||||
assert!(token.lifetime().expires() > &UTC::now());
|
assert!(token.lifetime().expires() > &UTC::now());
|
||||||
|
@ -90,7 +113,7 @@ fn request_token_bearer_expiring_success() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn refresh_token_bearer_full() {
|
fn refresh_token_bearer_full() {
|
||||||
let (client, http_client) = mock_client!(provider::BearerExpiring, connector::BearerExpiring);
|
let (client, http_client) = mock_client!(provider::BearerRefresh, connector::BearerRefresh);
|
||||||
let token = client.request_token(&http_client, "code").unwrap();
|
let token = client.request_token(&http_client, "code").unwrap();
|
||||||
let token = client.refresh_token(&http_client, token, None).unwrap();
|
let token = client.refresh_token(&http_client, token, None).unwrap();
|
||||||
assert_eq!("cccccccc", token.access_token());
|
assert_eq!("cccccccc", token.access_token());
|
||||||
|
@ -103,7 +126,7 @@ fn refresh_token_bearer_full() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn refresh_token_bearer_partial() {
|
fn refresh_token_bearer_partial() {
|
||||||
let (client, http_client) = mock_client!(provider::BearerExpiring, connector::BearerExpiringPartial);
|
let (client, http_client) = mock_client!(provider::BearerRefresh, connector::BearerRefreshPartial);
|
||||||
let token = client.request_token(&http_client, "code").unwrap();
|
let token = client.request_token(&http_client, "code").unwrap();
|
||||||
let token = client.refresh_token(&http_client, token, None).unwrap();
|
let token = client.refresh_token(&http_client, token, None).unwrap();
|
||||||
assert_eq!("cccccccc", token.access_token());
|
assert_eq!("cccccccc", token.access_token());
|
||||||
|
@ -116,14 +139,21 @@ fn refresh_token_bearer_partial() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn request_token_bearer_static_wrong_lifetime() {
|
fn request_token_bearer_static_wrong_lifetime() {
|
||||||
let (client, http_client) = mock_client!(provider::BearerStatic, connector::BearerExpiring);
|
let (client, http_client) = mock_client!(provider::BearerStatic, connector::BearerRefresh);
|
||||||
let err = client.request_token(&http_client, "code").unwrap_err();
|
let err = client.request_token(&http_client, "code").unwrap_err();
|
||||||
assert!(match err { ClientError::Parse(..) => true, _ => false });
|
assert!(match err { ClientError::Parse(..) => true, _ => false });
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn request_token_bearer_expiring_wrong_lifetime() {
|
fn request_token_bearer_expiring_wrong_lifetime() {
|
||||||
let (client, http_client) = mock_client!(provider::BearerExpiring, connector::BearerStatic);
|
let (client, http_client) = mock_client!(provider::BearerExpiring, connector::BearerRefresh);
|
||||||
|
let err = client.request_token(&http_client, "code").unwrap_err();
|
||||||
|
assert!(match err { ClientError::Parse(..) => true, _ => false });
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn request_token_bearer_refresh_wrong_lifetime() {
|
||||||
|
let (client, http_client) = mock_client!(provider::BearerRefresh, connector::BearerStatic);
|
||||||
let err = client.request_token(&http_client, "code").unwrap_err();
|
let err = client.request_token(&http_client, "code").unwrap_err();
|
||||||
assert!(match err { ClientError::Parse(..) => true, _ => false });
|
assert!(match err { ClientError::Parse(..) => true, _ => false });
|
||||||
}
|
}
|
||||||
|
@ -145,7 +175,7 @@ fn request_token_invalid_request() {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn refresh_token_invalid_request() {
|
fn refresh_token_invalid_request() {
|
||||||
let (client, http_client) = mock_client!(provider::BearerExpiring, connector::RefreshInvalidRequest);
|
let (client, http_client) = mock_client!(provider::BearerRefresh, connector::RefreshInvalidRequest);
|
||||||
let token = client.request_token(&http_client, "code").unwrap();
|
let token = client.request_token(&http_client, "code").unwrap();
|
||||||
let err = client.refresh_token(&http_client, token, None).unwrap_err();
|
let err = client.refresh_token(&http_client, token, None).unwrap_err();
|
||||||
assert!(match err {
|
assert!(match err {
|
||||||
|
|
|
@ -7,6 +7,5 @@ Pragma: no-cache
|
||||||
"access_token":"aaaaaaaa",
|
"access_token":"aaaaaaaa",
|
||||||
"token_type":"bearer",
|
"token_type":"bearer",
|
||||||
"expires_in":3600,
|
"expires_in":3600,
|
||||||
"refresh_token":"bbbbbbbb",
|
|
||||||
"scope":"example"
|
"scope":"example"
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
HTTP/1.1 200 OK
|
||||||
|
Content-Type: application/json;charset=UTF-8
|
||||||
|
Cache-Control: no-store
|
||||||
|
Pragma: no-cache
|
||||||
|
|
||||||
|
{
|
||||||
|
"access_token":"aaaaaaaa",
|
||||||
|
"token_type":"bearer",
|
||||||
|
"expires_in":3600,
|
||||||
|
"refresh_token":"bbbbbbbb",
|
||||||
|
"scope":"example"
|
||||||
|
}
|
Loading…
Reference in New Issue