From 97ab75fb151039e164b0f9ff3c13dae93ef0aa9f Mon Sep 17 00:00:00 2001 From: Quentin Gliech Date: Tue, 11 Jan 2022 12:54:26 +0100 Subject: [PATCH] Add loads of server metadata in the discovery document --- crates/handlers/src/oauth2/discovery.rs | 142 +++++++++++++++++++----- crates/oauth2-types/src/oidc.rs | 20 +++- 2 files changed, 132 insertions(+), 30 deletions(-) diff --git a/crates/handlers/src/oauth2/discovery.rs b/crates/handlers/src/oauth2/discovery.rs index e0cde5f4..36603535 100644 --- a/crates/handlers/src/oauth2/discovery.rs +++ b/crates/handlers/src/oauth2/discovery.rs @@ -16,20 +16,60 @@ use std::collections::HashSet; use mas_config::OAuth2Config; use oauth2_types::{ - oidc::{Metadata, SigningAlgorithm}, + oidc::{ClaimType, Metadata, SigningAlgorithm, SubjectType}, pkce::CodeChallengeMethod, - requests::{ClientAuthenticationMethod, GrantType, ResponseMode}, + requests::{ClientAuthenticationMethod, Display, GrantType, ResponseMode}, }; use warp::{filters::BoxedFilter, Filter, Reply}; +#[allow(clippy::too_many_lines)] pub(super) fn filter(config: &OAuth2Config) -> BoxedFilter<(Box,)> { let base = config.issuer.clone(); - let response_modes_supported = Some({ + // This is how clients can authenticate + let client_auth_methods_supported = Some({ let mut s = HashSet::new(); - s.insert(ResponseMode::FormPost); - s.insert(ResponseMode::Query); - s.insert(ResponseMode::Fragment); + s.insert(ClientAuthenticationMethod::ClientSecretBasic); + s.insert(ClientAuthenticationMethod::ClientSecretPost); + s.insert(ClientAuthenticationMethod::ClientSecretJwt); + s.insert(ClientAuthenticationMethod::PrivateKeyJwt); + s.insert(ClientAuthenticationMethod::None); + s + }); + + let client_auth_signing_alg_values_supported = Some({ + let mut s = HashSet::new(); + s.insert(SigningAlgorithm::Hs256); + s.insert(SigningAlgorithm::Hs384); + s.insert(SigningAlgorithm::Hs512); + s.insert(SigningAlgorithm::Rs256); + s.insert(SigningAlgorithm::Rs384); + s.insert(SigningAlgorithm::Rs512); + s + }); + + // This is how we can sign stuff + // TODO: query the signing store + let jwt_signing_alg_values_supported = Some({ + let mut s = HashSet::new(); + s.insert(SigningAlgorithm::Rs256); + s.insert(SigningAlgorithm::Rs384); + s.insert(SigningAlgorithm::Rs512); + s.insert(SigningAlgorithm::Es256); + s + }); + + // Prepare all the endpoints + let issuer = Some(base.clone()); + let authorization_endpoint = base.join("oauth2/authorize").ok(); + let token_endpoint = base.join("oauth2/token").ok(); + let jwks_uri = base.join("oauth2/keys.json").ok(); + let introspection_endpoint = base.join("oauth2/introspect").ok(); + let userinfo_endpoint = base.join("oauth2/userinfo").ok(); + + let scopes_supported = Some({ + let mut s = HashSet::new(); + s.insert("openid".to_string()); s }); @@ -45,6 +85,14 @@ pub(super) fn filter(config: &OAuth2Config) -> BoxedFilter<(Box,)> { s }); + let response_modes_supported = Some({ + let mut s = HashSet::new(); + s.insert(ResponseMode::FormPost); + s.insert(ResponseMode::Query); + s.insert(ResponseMode::Fragment); + s + }); + let grant_types_supported = Some({ let mut s = HashSet::new(); s.insert(GrantType::AuthorizationCode); @@ -52,22 +100,13 @@ pub(super) fn filter(config: &OAuth2Config) -> BoxedFilter<(Box,)> { s }); - let token_endpoint_auth_methods_supported = Some({ - let mut s = HashSet::new(); - s.insert(ClientAuthenticationMethod::ClientSecretBasic); - s.insert(ClientAuthenticationMethod::ClientSecretPost); - s.insert(ClientAuthenticationMethod::ClientSecretJwt); - s.insert(ClientAuthenticationMethod::None); - s - }); + let token_endpoint_auth_methods_supported = client_auth_methods_supported.clone(); + let token_endpoint_auth_signing_alg_values_supported = + client_auth_signing_alg_values_supported.clone(); - let token_endpoint_auth_signing_alg_values_supported = Some({ - let mut s = HashSet::new(); - s.insert(SigningAlgorithm::Hs256); - s.insert(SigningAlgorithm::Hs384); - s.insert(SigningAlgorithm::Hs512); - s - }); + let introspection_endpoint_auth_methods_supported = client_auth_methods_supported; + let introspection_endpoint_auth_signing_alg_values_supported = + client_auth_signing_alg_values_supported; let code_challenge_methods_supported = Some({ let mut s = HashSet::new(); @@ -76,19 +115,68 @@ pub(super) fn filter(config: &OAuth2Config) -> BoxedFilter<(Box,)> { s }); + let subject_types_supported = Some({ + let mut s = HashSet::new(); + s.insert(SubjectType::Public); + s + }); + + let id_token_signing_alg_values_supported = jwt_signing_alg_values_supported; + + let display_values_supported = Some({ + let mut s = HashSet::new(); + s.insert(Display::Page); + s + }); + + let claim_types_supported = Some({ + let mut s = HashSet::new(); + s.insert(ClaimType::Normal); + s + }); + + let claims_supported = Some({ + let mut s = HashSet::new(); + s.insert("iss".to_string()); + s.insert("sub".to_string()); + s.insert("aud".to_string()); + s.insert("iat".to_string()); + s.insert("exp".to_string()); + s.insert("nonce".to_string()); + s.insert("auth_time".to_string()); + s.insert("at_hash".to_string()); + s.insert("c_hash".to_string()); + s + }); + + let claims_parameter_supported = Some(false); + let request_parameter_supported = Some(false); + let request_uri_parameter_supported = Some(false); + let metadata = Metadata { - authorization_endpoint: base.join("oauth2/authorize").ok(), - token_endpoint: base.join("oauth2/token").ok(), - jwks_uri: base.join("oauth2/keys.json").ok(), - introspection_endpoint: base.join("oauth2/introspect").ok(), - userinfo_endpoint: base.join("oauth2/userinfo").ok(), - issuer: Some(base), + issuer, + authorization_endpoint, + token_endpoint, + jwks_uri, + scopes_supported, response_types_supported, response_modes_supported, grant_types_supported, token_endpoint_auth_methods_supported, token_endpoint_auth_signing_alg_values_supported, + introspection_endpoint, + introspection_endpoint_auth_methods_supported, + introspection_endpoint_auth_signing_alg_values_supported, code_challenge_methods_supported, + userinfo_endpoint, + subject_types_supported, + id_token_signing_alg_values_supported, + display_values_supported, + claim_types_supported, + claims_supported, + claims_parameter_supported, + request_parameter_supported, + request_uri_parameter_supported, ..Metadata::default() }; diff --git a/crates/oauth2-types/src/oidc.rs b/crates/oauth2-types/src/oidc.rs index 394e6bcf..2b5c2e97 100644 --- a/crates/oauth2-types/src/oidc.rs +++ b/crates/oauth2-types/src/oidc.rs @@ -23,6 +23,21 @@ use crate::{ requests::{ClientAuthenticationMethod, Display, GrantType, ResponseMode}, }; +#[derive(Serialize, Clone, Copy, PartialEq, Eq, Hash)] +#[serde(rename_all = "lowercase")] +pub enum SubjectType { + Public, + Pairwise, +} + +#[derive(Serialize, Clone, Copy, PartialEq, Eq, Hash)] +#[serde(rename_all = "lowercase")] +pub enum ClaimType { + Normal, + Aggregated, + Distributed, +} + #[derive(Serialize, Clone, Copy, PartialEq, Eq, Hash)] #[serde(rename_all = "UPPERCASE")] pub enum SigningAlgorithm { @@ -146,7 +161,7 @@ pub struct Metadata { /// JSON array containing a list of the Subject Identifier types that this /// OP supports. - pub subject_types_supported: Option>, + pub subject_types_supported: Option>, /// JSON array containing a list of the JWS "alg" values supported by the OP /// for the ID Token. @@ -194,10 +209,9 @@ pub struct Metadata { /// OpenID Provider supports. pub display_values_supported: Option>, - // TODO: type /// JSON array containing a list of the Claim Types that the OpenID Provider /// supports. - pub claim_types_supported: Option>, + pub claim_types_supported: Option>, /// JSON array containing a list of the Claim Names of the Claims that the /// OpenID Provider MAY be able to supply values for.