Skip to content

Commit

Permalink
feat(common): Token type serialization support
Browse files Browse the repository at this point in the history
This works by storing only timestamps.
Some convenience is provided as well.
  • Loading branch information
Byron committed Feb 27, 2015
1 parent aedc9b6 commit 1f655b4
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 34 deletions.
11 changes: 5 additions & 6 deletions examples/auth.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![feature(env, collections)]
extern crate "yup-oauth2" as oauth2;
extern crate "yup-hyper-mock" as mock;
extern crate hyper;
Expand All @@ -6,14 +7,12 @@ extern crate getopts;

use chrono::{Local};
use getopts::{HasArg,Options,Occur,Fail};
use std::os;
use std::old_io::{File, FileMode, FileAccess};
use std::old_path::Path;
use std::env;

fn usage(program: &str, opts: &Options, err: Option<Fail>) {
if err.is_some() {
println!("{}", err.unwrap());
os::set_exit_status(1);
env::set_exit_status(1);
}
println!("{}", opts.short_usage(program) + " SCOPE [SCOPE ...]");
println!("{}", opts.usage("A program to authenticate against oauthv2 services.\n\
Expand All @@ -22,7 +21,7 @@ fn usage(program: &str, opts: &Options, err: Option<Fail>) {
}

fn main() {
let args = os::args();
let args: Vec<String> = env::args().collect();
let prog = args[0].clone();

let mut opts = Options::new();
Expand Down Expand Up @@ -70,6 +69,6 @@ fn main() {
println!("{:?}", t);
} else {
println!("Invalid client id, invalid scope, user denied access or request expired");
os::set_exit_status(10);
env::set_exit_status(10);
}
}
68 changes: 59 additions & 9 deletions src/common.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,70 @@
use chrono::{DateTime, UTC};
use chrono::{DateTime, UTC, TimeZone};

/// Represents a token as returned by OAuth2 servers.
///
/// It is produced by all authentication flows.
/// It authenticates certain operations, and must be refreshed once
/// it reached it's expirey date.
#[derive(Clone, PartialEq, Debug)]
/// it reached it's expiry date.
///
/// The type is tuned to be suitable for direct de-serialization from server
/// replies, as well as for serialization for later reuse. This is the reason
/// for the two fields dealing with expiry - once in relative in and once in
/// absolute terms.
///
/// Utility methods make common queries easier, see `invalid()` or `expired()`.
#[derive(Clone, PartialEq, Debug, RustcDecodable, RustcEncodable)]
pub struct Token {
/// used when authenticating calls to oauth2 enabled services
/// used when authenticating calls to oauth2 enabled services.
pub access_token: String,
/// used to refresh an expired access_token
/// used to refresh an expired access_token.
pub refresh_token: String,
/// The token type as string - usually 'Bearer'
/// The token type as string - usually 'Bearer'.
pub token_type: String,
/// access_token is not valid for use after this date
pub expires_at: DateTime<UTC>
/// access_token will expire after this amount of time.
/// Prefer using expiry_date()
pub expires_in: Option<i64>,

/// timestamp is seconds since epoch indicating when the token will expire in absolute terms.
/// use expiry_date() to convert to DateTime.
pub expires_in_timestamp: Option<i64>,
}

impl Token {

/// Returns true if the access token is expired or unset.
pub fn invalid(&self) -> bool {
self.access_token.len() == 0
|| self.refresh_token.len() == 0
|| self.expired()
}

/// Returns true if we are expired.
///
/// # Panics
/// * if our access_token is unset
pub fn expired(&self) -> bool {
if self.access_token.len() == 0 || self.refresh_token.len() == 0 {
panic!("called expired() on unset token");
}
self.expiry_date() <= UTC::now()
}

/// Returns a DateTime object representing our expiry date.
pub fn expiry_date(&self) -> DateTime<UTC> {
UTC.timestamp(self.expires_in_timestamp.unwrap(), 0)
}

/// Adjust our stored expiry format to be absolute, using the current time.
pub fn set_expiry_absolute(&mut self) -> &mut Token {
if self.expires_in_timestamp.is_some() {
assert!(self.expires_in.is_none());
return self
}

self.expires_in_timestamp = Some(UTC::now().timestamp() + self.expires_in.unwrap());
self.expires_in = None;
self
}
}

/// All known authentication types, for suitable constants
Expand Down Expand Up @@ -74,7 +124,7 @@ mod tests {
fn console_secret() {
use rustc_serialize::json;
match json::decode::<ConsoleApplicationSecret>(SECRET) {
Ok(s) => assert!(s.installed.is_some()),
Ok(s) => assert!(s.installed.is_some() && s.web.is_none()),
Err(err) => panic!(err),
}
}
Expand Down
18 changes: 3 additions & 15 deletions src/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,14 +261,6 @@ impl<NC> DeviceFlow<NC>
}
};

#[derive(RustcDecodable)]
struct JsonToken {
access_token: String,
refresh_token: String,
token_type: String,
expires_in: i64,
}

#[derive(RustcDecodable)]
struct JsonError {
error: String
Expand All @@ -288,13 +280,9 @@ impl<NC> DeviceFlow<NC>
}

// yes, we expect that !
let t: JsonToken = json::decode(&json_str).unwrap();
self.state = PollResult::AccessGranted(Token {
access_token: t.access_token,
refresh_token: t.refresh_token,
token_type: t.token_type,
expires_at: UTC::now() + Duration::seconds(t.expires_in)
});
let mut t: Token = json::decode(&json_str).unwrap();
t.set_expiry_absolute();
self.state = PollResult::AccessGranted(t);
},
// In any other state, we will bail out and do nothing
_ => {}
Expand Down
10 changes: 6 additions & 4 deletions src/refresh.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use common::AuthenticationType;

use std::time::Duration;
use chrono::UTC;
use hyper;
use hyper::header::ContentType;
Expand Down Expand Up @@ -106,7 +105,8 @@ impl<NC> RefreshFlow<NC>
access_token: t.access_token,
token_type: t.token_type,
refresh_token: refresh_token.to_string(),
expires_at: UTC::now() + Duration::seconds(t.expires_in)
expires_in: None,
expires_in_timestamp: Some(UTC::now().timestamp() + t.expires_in),
});

&self.result
Expand Down Expand Up @@ -142,8 +142,10 @@ mod tests {

match *flow.refresh_token(AuthenticationType::Device,
"bogus", "secret", "bogus_refresh_token") {
RefreshResult::Success(ref t) => assert_eq!(t.access_token,
"1/fFAGRNJru1FTz70BzhT3Zg"),
RefreshResult::Success(ref t) => {
assert_eq!(t.access_token, "1/fFAGRNJru1FTz70BzhT3Zg");
assert!(!t.expired() && !t.invalid());
},
_ => unreachable!()
}
}
Expand Down

0 comments on commit 1f655b4

Please sign in to comment.