From 6b69d34ac8cf5fd4927ae5f83dd7731ee9ebf045 Mon Sep 17 00:00:00 2001 From: Matthew Scheirer Date: Sat, 16 Sep 2017 21:28:55 -0400 Subject: [PATCH] First complete impl without warns --- src/client.rs | 22 ++++++++++++++++------ src/discovery.rs | 23 +++++++++++++---------- src/error.rs | 2 +- src/lib.rs | 2 ++ src/token.rs | 17 +++++++---------- 5 files changed, 39 insertions(+), 27 deletions(-) diff --git a/src/client.rs b/src/client.rs index 6a366df..d87443a 100644 --- a/src/client.rs +++ b/src/client.rs @@ -4,7 +4,8 @@ use biscuit::jwk::{AlgorithmParameters, JWKSet}; use biscuit::jws::{Compact, Secret}; use chrono::{Duration, Utc}; use inth_oauth2; -use reqwest::{self, Url}; +use inth_oauth2::token::Token as _t; +use reqwest::{self, header, Url}; use url_serde; use validator::Validate; @@ -12,7 +13,7 @@ use std::collections::HashSet; use discovery::{self, Config, Discovered}; use error::{self, Decode, Error, Expiry, Mismatch, Missing, Validation}; -use token::{Claims, Expiring, Token}; +use token::{Claims, Token}; type IdToken = Compact; @@ -165,7 +166,7 @@ impl Client { pub fn request_token(&self, client: &reqwest::Client, auth_code: &str, - ) -> Result, error::Oauth> { + ) -> Result { self.oauth.request_token(client, auth_code) } @@ -218,7 +219,7 @@ impl Client { /// Given an auth_code and auth options, request the token, decode, and validate it. pub fn authenticate(&self, auth_code: &str, options: &Options - ) -> Result, Error> { + ) -> Result { let client = reqwest::Client::new()?; let mut token = self.request_token(&client, auth_code)?; self.decode_token(&mut token.id_token)?; @@ -347,13 +348,22 @@ impl Client { Ok(()) } - pub fn request_userinfo(&self, client: &reqwest::Client, token: &Token) -> Result { + pub fn request_userinfo(&self, client: &reqwest::Client, token: &Token) -> Result { match self.config().userinfo_endpoint { Some(ref url) => { + discovery::secure(&url)?; if url.origin() != self.config().issuer.origin() { return Err(error::Userinfo::MismatchIssuer.into()); } - unimplemented!() + let claims = token.id_token.payload()?; + let auth_code = token.access_token().to_string(); + let mut resp = client.get(url.clone())? + .header(header::Authorization(header::Bearer { token: auth_code })).send()?; + let info: Userinfo = resp.json()?; + if claims.sub != info.sub { + return Err(error::Userinfo::MismatchSubject.into()) + } + Ok(info) } None => Err(error::Userinfo::NoUrl.into()) } diff --git a/src/discovery.rs b/src/discovery.rs index 4fa2648..382dce7 100644 --- a/src/discovery.rs +++ b/src/discovery.rs @@ -1,12 +1,21 @@ use biscuit::Empty; use biscuit::jwk::JWKSet; use inth_oauth2::provider::Provider; +use inth_oauth2::token::Expiring; use reqwest::{Client, Url}; use url_serde; use validator::Validate; use error::Error; -use token::{Expiring, Token}; +use token::Token; + +pub(crate) fn secure(url: &Url) -> Result<(), Error> { + if url.scheme() != "https" { + Err(Error::Insecure) + } else { + Ok(()) + } +} #[derive(Deserialize, Serialize)] pub struct Config { @@ -114,7 +123,7 @@ pub struct Discovered { impl Provider for Discovered { type Lifetime = Expiring; - type Token = Token; + type Token = Token; fn auth_uri(&self) -> &str { self.config.authorization_endpoint.as_ref() } @@ -127,10 +136,7 @@ impl Provider for Discovered { /// Get the discovery config document from the given issuer url. Errors are either a reqwest error /// or an Insecure if the Url isn't https. pub fn discover(client: &Client, issuer: Url) -> Result { - if issuer.scheme() != "https" { - return Err(Error::Insecure) - } - + secure(&issuer)?; let mut resp = client.get(issuer)?.send()?; resp.json().map_err(Error::from) } @@ -138,10 +144,7 @@ pub fn discover(client: &Client, issuer: Url) -> Result { /// Get the JWK set from the given Url. Errors are either a reqwest error or an Insecure error if /// the url isn't https. pub fn jwks(client: &Client, url: Url) -> Result, Error> { - if url.scheme() != "https" { - return Err(Error::Insecure) - } - + secure(&url)?; let mut resp = client.get(url)?.send()?; resp.json().map_err(Error::from) } diff --git a/src/error.rs b/src/error.rs index 55bb1e5..b38b11f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -71,6 +71,6 @@ pub enum Expiry { #[derive(Debug)] pub enum Userinfo { NoUrl, + MismatchIssuer, MismatchSubject, - } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 2aae256..38f9d3a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,8 @@ extern crate biscuit; extern crate chrono; extern crate inth_oauth2; extern crate reqwest; +// We never use serde, but serde_derive needs it here +#[allow(unused_extern_crates)] extern crate serde; #[macro_use] extern crate serde_derive; diff --git a/src/token.rs b/src/token.rs index 5c689dc..5144842 100644 --- a/src/token.rs +++ b/src/token.rs @@ -2,14 +2,11 @@ use base64; use biscuit::{CompactJson, Empty, SingleOrMultiple}; use biscuit::jws::Compact; use inth_oauth2::client::response::{FromResponse, ParseError}; -use inth_oauth2::token::{self, Bearer, Lifetime}; +use inth_oauth2::token::{self, Bearer, Expiring}; use reqwest::Url; use serde_json::Value; use url_serde; -/// Rexported lifetime token types from oauth -pub use inth_oauth2::token::{Expiring, Refresh, Static}; - type IdToken = Compact; #[derive(Serialize, Deserialize)] @@ -60,12 +57,12 @@ impl CompactJson for Claims {} /// An OpenID Connect token. This is the only token allowed by spec. /// Has an access_token for bearer, and the id_token for authentication. /// Wraps an oauth bearer token. -pub struct Token { - bearer: Bearer, +pub struct Token { + bearer: Bearer, pub id_token: IdToken, } -impl Token { +impl Token { // Takes a json response object and parses out the id token // TODO Support extracting a jwe token according to spec. Right now we only support jws tokens. fn id_token(json: &Value) -> Result { @@ -77,19 +74,19 @@ impl Token { } } -impl token::Token for Token { +impl token::Token for Token { fn access_token(&self) -> &str { self.bearer.access_token() } fn scope(&self) -> Option<&str> { self.bearer.scope() } - fn lifetime(&self) -> &L { + fn lifetime(&self) -> &Expiring { self.bearer.lifetime() } } -impl FromResponse for Token { +impl FromResponse for Token { fn from_response(json: &Value) -> Result { let bearer = Bearer::from_response(json)?; let id_token = Self::id_token(json)?;