First complete impl without warns

This commit is contained in:
Matthew Scheirer 2017-09-16 21:28:55 -04:00
parent 08d7765644
commit 6b69d34ac8
5 changed files with 39 additions and 27 deletions

View File

@ -4,7 +4,8 @@ use biscuit::jwk::{AlgorithmParameters, JWKSet};
use biscuit::jws::{Compact, Secret}; use biscuit::jws::{Compact, Secret};
use chrono::{Duration, Utc}; use chrono::{Duration, Utc};
use inth_oauth2; use inth_oauth2;
use reqwest::{self, Url}; use inth_oauth2::token::Token as _t;
use reqwest::{self, header, Url};
use url_serde; use url_serde;
use validator::Validate; use validator::Validate;
@ -12,7 +13,7 @@ use std::collections::HashSet;
use discovery::{self, Config, Discovered}; use discovery::{self, Config, Discovered};
use error::{self, Decode, Error, Expiry, Mismatch, Missing, Validation}; use error::{self, Decode, Error, Expiry, Mismatch, Missing, Validation};
use token::{Claims, Expiring, Token}; use token::{Claims, Token};
type IdToken = Compact<Claims, Empty>; type IdToken = Compact<Claims, Empty>;
@ -165,7 +166,7 @@ impl Client {
pub fn request_token(&self, pub fn request_token(&self,
client: &reqwest::Client, client: &reqwest::Client,
auth_code: &str, auth_code: &str,
) -> Result<Token<Expiring>, error::Oauth> { ) -> Result<Token, error::Oauth> {
self.oauth.request_token(client, auth_code) 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. /// Given an auth_code and auth options, request the token, decode, and validate it.
pub fn authenticate(&self, auth_code: &str, options: &Options pub fn authenticate(&self, auth_code: &str, options: &Options
) -> Result<Token<Expiring>, Error> { ) -> Result<Token, Error> {
let client = reqwest::Client::new()?; let client = reqwest::Client::new()?;
let mut token = self.request_token(&client, auth_code)?; let mut token = self.request_token(&client, auth_code)?;
self.decode_token(&mut token.id_token)?; self.decode_token(&mut token.id_token)?;
@ -347,13 +348,22 @@ impl Client {
Ok(()) Ok(())
} }
pub fn request_userinfo(&self, client: &reqwest::Client, token: &Token<Expiring>) -> Result<Userinfo, Error> { pub fn request_userinfo(&self, client: &reqwest::Client, token: &Token) -> Result<Userinfo, Error> {
match self.config().userinfo_endpoint { match self.config().userinfo_endpoint {
Some(ref url) => { Some(ref url) => {
discovery::secure(&url)?;
if url.origin() != self.config().issuer.origin() { if url.origin() != self.config().issuer.origin() {
return Err(error::Userinfo::MismatchIssuer.into()); 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()) None => Err(error::Userinfo::NoUrl.into())
} }

View File

@ -1,12 +1,21 @@
use biscuit::Empty; use biscuit::Empty;
use biscuit::jwk::JWKSet; use biscuit::jwk::JWKSet;
use inth_oauth2::provider::Provider; use inth_oauth2::provider::Provider;
use inth_oauth2::token::Expiring;
use reqwest::{Client, Url}; use reqwest::{Client, Url};
use url_serde; use url_serde;
use validator::Validate; use validator::Validate;
use error::Error; 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)] #[derive(Deserialize, Serialize)]
pub struct Config { pub struct Config {
@ -114,7 +123,7 @@ pub struct Discovered {
impl Provider for Discovered { impl Provider for Discovered {
type Lifetime = Expiring; type Lifetime = Expiring;
type Token = Token<Expiring>; type Token = Token;
fn auth_uri(&self) -> &str { fn auth_uri(&self) -> &str {
self.config.authorization_endpoint.as_ref() 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 /// 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. /// or an Insecure if the Url isn't https.
pub fn discover(client: &Client, issuer: Url) -> Result<Config, Error> { pub fn discover(client: &Client, issuer: Url) -> Result<Config, Error> {
if issuer.scheme() != "https" { secure(&issuer)?;
return Err(Error::Insecure)
}
let mut resp = client.get(issuer)?.send()?; let mut resp = client.get(issuer)?.send()?;
resp.json().map_err(Error::from) resp.json().map_err(Error::from)
} }
@ -138,10 +144,7 @@ pub fn discover(client: &Client, issuer: Url) -> Result<Config, Error> {
/// Get the JWK set from the given Url. Errors are either a reqwest error or an Insecure error if /// Get the JWK set from the given Url. Errors are either a reqwest error or an Insecure error if
/// the url isn't https. /// the url isn't https.
pub fn jwks(client: &Client, url: Url) -> Result<JWKSet<Empty>, Error> { pub fn jwks(client: &Client, url: Url) -> Result<JWKSet<Empty>, Error> {
if url.scheme() != "https" { secure(&url)?;
return Err(Error::Insecure)
}
let mut resp = client.get(url)?.send()?; let mut resp = client.get(url)?.send()?;
resp.json().map_err(Error::from) resp.json().map_err(Error::from)
} }

View File

@ -71,6 +71,6 @@ pub enum Expiry {
#[derive(Debug)] #[derive(Debug)]
pub enum Userinfo { pub enum Userinfo {
NoUrl, NoUrl,
MismatchIssuer,
MismatchSubject, MismatchSubject,
} }

View File

@ -3,6 +3,8 @@ extern crate biscuit;
extern crate chrono; extern crate chrono;
extern crate inth_oauth2; extern crate inth_oauth2;
extern crate reqwest; extern crate reqwest;
// We never use serde, but serde_derive needs it here
#[allow(unused_extern_crates)]
extern crate serde; extern crate serde;
#[macro_use] #[macro_use]
extern crate serde_derive; extern crate serde_derive;

View File

@ -2,14 +2,11 @@ use base64;
use biscuit::{CompactJson, Empty, SingleOrMultiple}; use biscuit::{CompactJson, Empty, SingleOrMultiple};
use biscuit::jws::Compact; use biscuit::jws::Compact;
use inth_oauth2::client::response::{FromResponse, ParseError}; 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 reqwest::Url;
use serde_json::Value; use serde_json::Value;
use url_serde; use url_serde;
/// Rexported lifetime token types from oauth
pub use inth_oauth2::token::{Expiring, Refresh, Static};
type IdToken = Compact<Claims, Empty>; type IdToken = Compact<Claims, Empty>;
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
@ -60,12 +57,12 @@ impl CompactJson for Claims {}
/// An OpenID Connect token. This is the only token allowed by spec. /// An OpenID Connect token. This is the only token allowed by spec.
/// Has an access_token for bearer, and the id_token for authentication. /// Has an access_token for bearer, and the id_token for authentication.
/// Wraps an oauth bearer token. /// Wraps an oauth bearer token.
pub struct Token<L: Lifetime> { pub struct Token {
bearer: Bearer<L>, bearer: Bearer<Expiring>,
pub id_token: IdToken, pub id_token: IdToken,
} }
impl<L: Lifetime> Token<L> { impl Token {
// Takes a json response object and parses out the id 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. // TODO Support extracting a jwe token according to spec. Right now we only support jws tokens.
fn id_token(json: &Value) -> Result<IdToken, ParseError> { fn id_token(json: &Value) -> Result<IdToken, ParseError> {
@ -77,19 +74,19 @@ impl<L: Lifetime> Token<L> {
} }
} }
impl<L: Lifetime> token::Token<L> for Token<L> { impl token::Token<Expiring> for Token {
fn access_token(&self) -> &str { fn access_token(&self) -> &str {
self.bearer.access_token() self.bearer.access_token()
} }
fn scope(&self) -> Option<&str> { fn scope(&self) -> Option<&str> {
self.bearer.scope() self.bearer.scope()
} }
fn lifetime(&self) -> &L { fn lifetime(&self) -> &Expiring {
self.bearer.lifetime() self.bearer.lifetime()
} }
} }
impl<L: Lifetime> FromResponse for Token<L> { impl FromResponse for Token {
fn from_response(json: &Value) -> Result<Self, ParseError> { fn from_response(json: &Value) -> Result<Self, ParseError> {
let bearer = Bearer::from_response(json)?; let bearer = Bearer::from_response(json)?;
let id_token = Self::id_token(json)?; let id_token = Self::id_token(json)?;