151 lines
5.2 KiB
Rust
151 lines
5.2 KiB
Rust
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::Token;
|
|
|
|
pub(crate) fn secure(url: &Url) -> Result<(), Error> {
|
|
if url.scheme() != "https" {
|
|
Err(Error::Insecure)
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize, Serialize)]
|
|
pub struct Config {
|
|
#[serde(with = "url_serde")]
|
|
pub issuer: Url,
|
|
#[serde(with = "url_serde")]
|
|
pub authorization_endpoint: Url,
|
|
#[serde(with = "url_serde")]
|
|
// Only optional in the implicit flow
|
|
// TODO For now, we only support code flows.
|
|
pub token_endpoint: Url,
|
|
#[serde(with = "url_serde")]
|
|
pub userinfo_endpoint: Option<Url>,
|
|
#[serde(with = "url_serde")]
|
|
pub jwks_uri: Url,
|
|
#[serde(with = "url_serde")]
|
|
pub registration_endpoint: Option<Url>,
|
|
pub scopes_supported: Option<Vec<String>>,
|
|
// There are only three valid response types, plus combinations of them, and none
|
|
// If we want to make these user friendly we want a struct to represent all 7 types
|
|
pub response_types_supported: Vec<String>,
|
|
// There are only two possible values here, query and fragment. Default is both.
|
|
pub response_modes_supported: Option<Vec<String>>,
|
|
// Must support at least authorization_code and implicit.
|
|
pub grant_types_supported: Option<Vec<String>>,
|
|
pub acr_values_supported: Option<Vec<String>>,
|
|
// pairwise and public are valid by spec, but servers can add more
|
|
pub subject_types_supported: Vec<String>,
|
|
// Must include at least RS256, none is only allowed with response types without id tokens
|
|
pub id_token_signing_alg_values_supported: Vec<String>,
|
|
pub id_token_encryption_alg_values_supported: Option<Vec<String>>,
|
|
pub id_token_encryption_enc_values_supported: Option<Vec<String>>,
|
|
pub userinfo_signing_alg_values_supported: Option<Vec<String>>,
|
|
pub userinfo_encryption_alg_values_supported: Option<Vec<String>>,
|
|
pub userinfo_encryption_enc_values_supported: Option<Vec<String>>,
|
|
pub request_object_signing_alg_values_supported: Option<Vec<String>>,
|
|
pub request_object_encryption_alg_values_supported: Option<Vec<String>>,
|
|
pub request_object_encryption_enc_values_supported: Option<Vec<String>>,
|
|
// Spec options are client_secret_post, client_secret_basic, client_secret_jwt, private_key_jwt
|
|
// If omitted, client_secret_basic is used
|
|
pub token_endpoint_auth_methods_supported: Option<Vec<String>>,
|
|
// Only wanted with jwt auth methods, should have RS256, none not allowed
|
|
pub token_endpoint_auth_signing_alg_values_supported: Option<Vec<String>>,
|
|
pub display_values_supported: Option<Vec<String>>,
|
|
// Valid options are normal, aggregated, and distributed. If omitted, only use normal
|
|
pub claim_types_supported: Option<Vec<String>>,
|
|
pub claims_supported: Option<Vec<Claim>>,
|
|
#[serde(with = "url_serde")]
|
|
pub service_documentation: Option<Url>,
|
|
pub claims_locales_supported: Option<Vec<String>>,
|
|
pub ui_locales_supported: Option<Vec<String>>,
|
|
// default false
|
|
pub claims_parameter_supported: Option<bool>,
|
|
// default false
|
|
pub request_parameter_supported: Option<bool>,
|
|
// default true
|
|
pub request_uri_parameter_supported: Option<bool>,
|
|
// default false
|
|
pub require_request_uri_registration: Option<bool>,
|
|
#[serde(with = "url_serde")]
|
|
pub op_policy_uri: Option<Url>,
|
|
#[serde(with = "url_serde")]
|
|
pub op_tos_uri: Option<Url>,
|
|
// This is a NONSTANDARD extension Google uses that is a part of the Oauth discovery draft
|
|
pub code_challenge_methods_supported: Option<Vec<String>>,
|
|
}
|
|
|
|
#[derive(Deserialize, Serialize)]
|
|
pub enum Claim {
|
|
Name(String),
|
|
FamilyName(String),
|
|
GivenName(String),
|
|
MiddleName(String),
|
|
Nickname(String),
|
|
PreferredUsername(String),
|
|
Profile(
|
|
#[serde(with = "url_serde")]
|
|
Url
|
|
),
|
|
Picture(
|
|
#[serde(with = "url_serde")]
|
|
Url
|
|
),
|
|
Website(
|
|
#[serde(with = "url_serde")]
|
|
Url
|
|
),
|
|
Gender(String),
|
|
Birthdate(String),
|
|
Zoneinfo(String),
|
|
Locale(String),
|
|
UpdatedAt(u64),
|
|
Email(Email),
|
|
}
|
|
|
|
#[derive(Debug, Deserialize, Serialize, Validate)]
|
|
pub struct Email {
|
|
#[validate(email)]
|
|
pub address: String,
|
|
}
|
|
|
|
pub struct Discovered {
|
|
pub config: Config,
|
|
}
|
|
|
|
impl Provider for Discovered {
|
|
type Lifetime = Expiring;
|
|
type Token = Token;
|
|
fn auth_uri(&self) -> &str {
|
|
self.config.authorization_endpoint.as_ref()
|
|
}
|
|
|
|
fn token_uri(&self) -> &str {
|
|
self.config.token_endpoint.as_ref()
|
|
}
|
|
}
|
|
|
|
/// 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<Config, Error> {
|
|
secure(&issuer)?;
|
|
let mut resp = client.get(issuer)?.send()?;
|
|
resp.json().map_err(Error::from)
|
|
}
|
|
|
|
/// 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<JWKSet<Empty>, Error> {
|
|
secure(&url)?;
|
|
let mut resp = client.get(url)?.send()?;
|
|
resp.json().map_err(Error::from)
|
|
}
|