diff --git a/crates/axum-utils/src/client_authorization.rs b/crates/axum-utils/src/client_authorization.rs index f994816c..dedf81cf 100644 --- a/crates/axum-utils/src/client_authorization.rs +++ b/crates/axum-utils/src/client_authorization.rs @@ -31,8 +31,8 @@ use mas_data_model::{Client, JwksOrJwksUri, StorageBackend}; use mas_http::HttpServiceExt; use mas_iana::oauth::OAuthClientAuthenticationMethod; use mas_jose::{ - DecodedJsonWebToken, DynamicJwksStore, Either, JsonWebKeySet, JsonWebSignatureHeader, - JsonWebTokenParts, SharedSecret, StaticJwksStore, VerifyingKeystore, + jwk::PublicJsonWebKeySet, DecodedJsonWebToken, DynamicJwksStore, Either, + JsonWebSignatureHeader, JsonWebTokenParts, SharedSecret, StaticJwksStore, VerifyingKeystore, }; use mas_storage::{ oauth2::client::{lookup_client_by_client_id, ClientFetchError}, @@ -185,7 +185,7 @@ fn jwks_key_store(jwks: &JwksOrJwksUri) -> Either() + .json_response::() .map_request(move |_: ()| { http::Request::builder() .method("GET") diff --git a/crates/config/src/sections/clients.rs b/crates/config/src/sections/clients.rs index ad4a4a6d..313df21c 100644 --- a/crates/config/src/sections/clients.rs +++ b/crates/config/src/sections/clients.rs @@ -16,7 +16,7 @@ use std::ops::{Deref, DerefMut}; use async_trait::async_trait; use mas_iana::oauth::OAuthClientAuthenticationMethod; -use mas_jose::JsonWebKeySet; +use mas_jose::jwk::PublicJsonWebKeySet; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_with::skip_serializing_none; @@ -28,12 +28,12 @@ use super::ConfigurationSection; #[derive(JsonSchema, Serialize, Deserialize, Clone, Debug)] #[serde(rename_all = "snake_case")] pub enum JwksOrJwksUri { - Jwks(JsonWebKeySet), + Jwks(PublicJsonWebKeySet), JwksUri(Url), } -impl From for JwksOrJwksUri { - fn from(jwks: JsonWebKeySet) -> Self { +impl From for JwksOrJwksUri { + fn from(jwks: PublicJsonWebKeySet) -> Self { Self::Jwks(jwks) } } @@ -125,7 +125,7 @@ impl ClientConfig { #[doc(hidden)] #[must_use] - pub fn jwks(&self) -> Option<&JsonWebKeySet> { + pub fn jwks(&self) -> Option<&PublicJsonWebKeySet> { match &self.client_auth_method { ClientAuthMethodConfig::PrivateKeyJwt(JwksOrJwksUri::Jwks(jwks)) => Some(jwks), _ => None, diff --git a/crates/data-model/src/oauth2/client.rs b/crates/data-model/src/oauth2/client.rs index f8ffdd92..c2768eb9 100644 --- a/crates/data-model/src/oauth2/client.rs +++ b/crates/data-model/src/oauth2/client.rs @@ -16,7 +16,7 @@ use mas_iana::{ jose::JsonWebSignatureAlg, oauth::{OAuthAuthorizationEndpointResponseType, OAuthClientAuthenticationMethod}, }; -use mas_jose::JsonWebKeySet; +use mas_jose::jwk::PublicJsonWebKeySet; use oauth2_types::requests::GrantType; use serde::Serialize; use thiserror::Error; @@ -28,7 +28,7 @@ use crate::traits::{StorageBackend, StorageBackendMarker}; #[serde(rename_all = "snake_case")] pub enum JwksOrJwksUri { /// Client's JSON Web Key Set document, passed by value. - Jwks(JsonWebKeySet), + Jwks(PublicJsonWebKeySet), /// URL for the Client's JSON Web Key Set document. JwksUri(Url), diff --git a/crates/jose/src/jwk/mod.rs b/crates/jose/src/jwk/mod.rs index acbe9dfe..7baf4769 100644 --- a/crates/jose/src/jwk/mod.rs +++ b/crates/jose/src/jwk/mod.rs @@ -34,6 +34,10 @@ pub(crate) mod public_parameters; pub use self::public_parameters::JsonWebKeyPublicParameters as JsonWebKeyParameters; +pub trait JwkKty { + fn kty(&self) -> JsonWebKeyType; +} + trait JwkEcCurve { const CRV: JsonWebKeyEcEllipticCurve; } @@ -53,9 +57,9 @@ impl JwkEcCurve for k256::Secp256k1 { #[serde_as] #[skip_serializing_none] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -pub struct JsonWebKey { +pub struct JsonWebKey

{ #[serde(flatten)] - parameters: JsonWebKeyParameters, + parameters: P, #[serde(default)] r#use: Option, @@ -89,9 +93,12 @@ pub struct JsonWebKey { x5t_s256: Option>, } -impl JsonWebKey { +pub type PublicJsonWebKey = JsonWebKey; +pub type PrivateJsonWebKey = JsonWebKey; + +impl

JsonWebKey

{ #[must_use] - pub const fn new(parameters: JsonWebKeyParameters) -> Self { + pub const fn new(parameters: P) -> Self { Self { parameters, r#use: None, @@ -135,30 +142,29 @@ impl JsonWebKey { } #[must_use] - pub const fn params(&self) -> &JsonWebKeyParameters { + pub const fn params(&self) -> &P { &self.parameters } } -impl Constrainable for JsonWebKey { +impl

Constrainable for JsonWebKey

+where + P: JwkKty, +{ fn kid(&self) -> Option<&str> { self.kid.as_deref() } fn kty(&self) -> JsonWebKeyType { - match self.parameters { - JsonWebKeyParameters::Ec { .. } => JsonWebKeyType::Ec, - JsonWebKeyParameters::Rsa { .. } => JsonWebKeyType::Rsa, - JsonWebKeyParameters::Okp { .. } => JsonWebKeyType::Okp, - } + self.parameters.kty() } fn algs(&self) -> Option> { if let Some(alg) = self.alg { Some(vec![alg]) } else { - match &self.parameters { - JsonWebKeyParameters::Rsa { .. } => Some(vec![ + match self.parameters.kty() { + JsonWebKeyType::Rsa => Some(vec![ JsonWebSignatureAlg::Rs256, JsonWebSignatureAlg::Rs384, JsonWebSignatureAlg::Rs512, @@ -166,13 +172,22 @@ impl Constrainable for JsonWebKey { JsonWebSignatureAlg::Ps384, JsonWebSignatureAlg::Ps512, ]), - JsonWebKeyParameters::Ec(params) => match params.crv() { - JsonWebKeyEcEllipticCurve::P256 => Some(vec![JsonWebSignatureAlg::Es256]), - JsonWebKeyEcEllipticCurve::P384 => Some(vec![JsonWebSignatureAlg::Es384]), - JsonWebKeyEcEllipticCurve::P521 => Some(vec![JsonWebSignatureAlg::Es512]), - JsonWebKeyEcEllipticCurve::Secp256K1 => Some(vec![JsonWebSignatureAlg::Es256K]), - }, - JsonWebKeyParameters::Okp { .. } => Some(vec![JsonWebSignatureAlg::EdDsa]), + JsonWebKeyType::Ec => { + todo!() + /* + match params.crv() { + JsonWebKeyEcEllipticCurve::P256 => Some(vec![JsonWebSignatureAlg::Es256]), + JsonWebKeyEcEllipticCurve::P384 => Some(vec![JsonWebSignatureAlg::Es384]), + JsonWebKeyEcEllipticCurve::P521 => Some(vec![JsonWebSignatureAlg::Es512]), + JsonWebKeyEcEllipticCurve::Secp256K1 => Some(vec![JsonWebSignatureAlg::Es256K]), + }, */ + } + JsonWebKeyType::Okp => Some(vec![JsonWebSignatureAlg::EdDsa]), + JsonWebKeyType::Oct => Some(vec![ + JsonWebSignatureAlg::Hs256, + JsonWebSignatureAlg::Hs384, + JsonWebSignatureAlg::Hs512, + ]), } } } @@ -183,21 +198,25 @@ impl Constrainable for JsonWebKey { } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] -pub struct JsonWebKeySet { - keys: Vec, +pub struct JsonWebKeySet

{ + keys: Vec>, } -impl std::ops::Deref for JsonWebKeySet { - type Target = Vec; +pub type PublicJsonWebKeySet = JsonWebKeySet; +pub type PrivateJsonWebKeySet = + JsonWebKeySet; + +impl

std::ops::Deref for JsonWebKeySet

{ + type Target = Vec>; fn deref(&self) -> &Self::Target { &self.keys } } -impl JsonWebKeySet { +impl

JsonWebKeySet

{ #[must_use] - pub fn new(keys: Vec) -> Self { + pub fn new(keys: Vec>) -> Self { Self { keys } } } diff --git a/crates/jose/src/jwk/private_parameters.rs b/crates/jose/src/jwk/private_parameters.rs index 2656f3ff..950ae41f 100644 --- a/crates/jose/src/jwk/private_parameters.rs +++ b/crates/jose/src/jwk/private_parameters.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use mas_iana::jose::{JsonWebKeyEcEllipticCurve, JsonWebKeyOkpEllipticCurve}; +use mas_iana::jose::{JsonWebKeyEcEllipticCurve, JsonWebKeyOkpEllipticCurve, JsonWebKeyType}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_with::{ @@ -21,6 +21,8 @@ use serde_with::{ serde_as, }; +use super::JwkKty; + #[serde_as] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] #[serde(tag = "kty")] @@ -38,6 +40,17 @@ pub enum JsonWebKeyPrivateParameters { Okp(OkpPrivateParameters), } +impl JwkKty for JsonWebKeyPrivateParameters { + fn kty(&self) -> JsonWebKeyType { + match self { + Self::Oct(_) => JsonWebKeyType::Oct, + Self::Rsa(_) => JsonWebKeyType::Rsa, + Self::Ec(_) => JsonWebKeyType::Ec, + Self::Okp(_) => JsonWebKeyType::Okp, + } + } +} + #[serde_as] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] pub struct OctPrivateParameters { diff --git a/crates/jose/src/jwk/public_parameters.rs b/crates/jose/src/jwk/public_parameters.rs index 7c1dfb12..563bb13a 100644 --- a/crates/jose/src/jwk/public_parameters.rs +++ b/crates/jose/src/jwk/public_parameters.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -use mas_iana::jose::{JsonWebKeyEcEllipticCurve, JsonWebKeyOkpEllipticCurve}; +use mas_iana::jose::{JsonWebKeyEcEllipticCurve, JsonWebKeyOkpEllipticCurve, JsonWebKeyType}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; use serde_with::{ @@ -21,6 +21,8 @@ use serde_with::{ serde_as, }; +use super::JwkKty; + #[serde_as] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] #[serde(tag = "kty")] @@ -35,6 +37,16 @@ pub enum JsonWebKeyPublicParameters { Okp(OkpPublicParameters), } +impl JwkKty for JsonWebKeyPublicParameters { + fn kty(&self) -> JsonWebKeyType { + match self { + Self::Rsa(_) => JsonWebKeyType::Rsa, + Self::Ec(_) => JsonWebKeyType::Ec, + Self::Okp(_) => JsonWebKeyType::Okp, + } + } +} + #[serde_as] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] pub struct RsaPublicParameters { diff --git a/crates/jose/src/jwt/header.rs b/crates/jose/src/jwt/header.rs index a64a44c4..ef03454e 100644 --- a/crates/jose/src/jwt/header.rs +++ b/crates/jose/src/jwt/header.rs @@ -21,7 +21,7 @@ use serde_with::{ }; use url::Url; -use crate::jwk::JsonWebKey; +use crate::jwk::PublicJsonWebKey; #[serde_as] #[skip_serializing_none] @@ -33,7 +33,7 @@ pub struct JsonWebSignatureHeader { jku: Option, #[serde(default)] - jwk: Option, + jwk: Option, #[serde(default)] kid: Option, @@ -98,12 +98,12 @@ impl JsonWebSignatureHeader { } #[must_use] - pub const fn jwk(&self) -> Option<&JsonWebKey> { + pub const fn jwk(&self) -> Option<&PublicJsonWebKey> { self.jwk.as_ref() } #[must_use] - pub fn with_jwk(mut self, jwk: JsonWebKey) -> Self { + pub fn with_jwk(mut self, jwk: PublicJsonWebKey) -> Self { self.jwk = Some(jwk); self } diff --git a/crates/jose/src/keystore/jwks/dynamic_store.rs b/crates/jose/src/keystore/jwks/dynamic_store.rs index 83aa3588..1448c583 100644 --- a/crates/jose/src/keystore/jwks/dynamic_store.rs +++ b/crates/jose/src/keystore/jwks/dynamic_store.rs @@ -24,7 +24,7 @@ use tower::{ }; use super::StaticJwksStore; -use crate::{JsonWebKeySet, JsonWebSignatureHeader, VerifyingKeystore}; +use crate::{jwk::PublicJsonWebKeySet, JsonWebSignatureHeader, VerifyingKeystore}; #[derive(Debug, Error)] pub enum Error { @@ -60,7 +60,7 @@ impl Default for State { } impl State { - fn fullfill(&mut self, key_set: JsonWebKeySet) { + fn fullfill(&mut self, key_set: PublicJsonWebKeySet) { *self = Self::Fulfilled { at: Utc::now(), store: StaticJwksStore::new(key_set), @@ -100,14 +100,14 @@ impl State { #[derive(Clone)] pub struct DynamicJwksStore { - exporter: BoxCloneService<(), JsonWebKeySet, BoxError>, + exporter: BoxCloneService<(), PublicJsonWebKeySet, BoxError>, cache: Arc>>>, } impl DynamicJwksStore { pub fn new(exporter: T) -> Self where - T: Service<(), Response = JsonWebKeySet, Error = BoxError> + Send + Clone + 'static, + T: Service<(), Response = PublicJsonWebKeySet, Error = BoxError> + Send + Clone + 'static, T::Future: Send, { Self { diff --git a/crates/jose/src/keystore/jwks/static_store.rs b/crates/jose/src/keystore/jwks/static_store.rs index 731d63c3..607cbe36 100644 --- a/crates/jose/src/keystore/jwks/static_store.rs +++ b/crates/jose/src/keystore/jwks/static_store.rs @@ -22,8 +22,9 @@ use signature::{Signature, Verifier}; use thiserror::Error; use crate::{ - constraints::Constrainable, JsonWebKey, JsonWebKeySet, JsonWebSignatureHeader, - VerifyingKeystore, + constraints::Constrainable, + jwk::{PublicJsonWebKey, PublicJsonWebKeySet}, + JsonWebSignatureHeader, VerifyingKeystore, }; #[derive(Debug, Error)] @@ -60,7 +61,7 @@ struct KeyConstraint<'a> { } impl<'a> KeyConstraint<'a> { - fn matches(&self, key: &'a JsonWebKey) -> bool { + fn matches(&self, key: &'a PublicJsonWebKey) -> bool { // If a specific KID was asked, match the key only if it has a matching kid // field if let Some(kid) = self.kid { @@ -84,22 +85,25 @@ impl<'a> KeyConstraint<'a> { true } - fn find_keys(&self, key_set: &'a JsonWebKeySet) -> Vec<&'a JsonWebKey> { + fn find_keys(&self, key_set: &'a PublicJsonWebKeySet) -> Vec<&'a PublicJsonWebKey> { key_set.iter().filter(|k| self.matches(k)).collect() } } pub struct StaticJwksStore { - key_set: JsonWebKeySet, + key_set: PublicJsonWebKeySet, } impl StaticJwksStore { #[must_use] - pub fn new(key_set: JsonWebKeySet) -> Self { + pub fn new(key_set: PublicJsonWebKeySet) -> Self { Self { key_set } } - fn find_key<'a>(&'a self, constraint: &KeyConstraint<'a>) -> Result<&'a JsonWebKey, Error> { + fn find_key<'a>( + &'a self, + constraint: &KeyConstraint<'a>, + ) -> Result<&'a PublicJsonWebKey, Error> { let keys = constraint.find_keys(&self.key_set); match &keys[..] { diff --git a/crates/jose/src/keystore/static_keystore.rs b/crates/jose/src/keystore/static_keystore.rs index 5699ea1e..7b432703 100644 --- a/crates/jose/src/keystore/static_keystore.rs +++ b/crates/jose/src/keystore/static_keystore.rs @@ -34,7 +34,7 @@ use signature::{Signature, Signer, Verifier}; use tower::Service; use super::{SigningKeystore, VerifyingKeystore}; -use crate::{JsonWebKey, JsonWebKeySet, JsonWebSignatureHeader}; +use crate::{jwk::PublicJsonWebKeySet, JsonWebKey, JsonWebKeySet, JsonWebSignatureHeader}; // Generate with // openssl genrsa 2048 @@ -365,7 +365,7 @@ impl VerifyingKeystore for StaticKeystore { impl Service<()> for &StaticKeystore { type Future = Ready>; - type Response = JsonWebKeySet; + type Response = PublicJsonWebKeySet; type Error = Infallible; fn poll_ready(&mut self, _cx: &mut std::task::Context<'_>) -> Poll> { diff --git a/crates/jose/src/lib.rs b/crates/jose/src/lib.rs index 855b7145..8f05b4fa 100644 --- a/crates/jose/src/lib.rs +++ b/crates/jose/src/lib.rs @@ -20,7 +20,7 @@ pub mod claims; pub mod constraints; pub mod hmac; -pub(crate) mod jwk; +pub mod jwk; pub(crate) mod jwt; mod keystore; pub(crate) mod rsa; diff --git a/crates/oauth2-types/src/registration/client_metadata_serde.rs b/crates/oauth2-types/src/registration/client_metadata_serde.rs index f866ba67..da6a7150 100644 --- a/crates/oauth2-types/src/registration/client_metadata_serde.rs +++ b/crates/oauth2-types/src/registration/client_metadata_serde.rs @@ -20,7 +20,7 @@ use mas_iana::{ jose::{JsonWebEncryptionAlg, JsonWebEncryptionEnc, JsonWebSignatureAlg}, oauth::{OAuthAuthorizationEndpointResponseType, OAuthClientAuthenticationMethod}, }; -use mas_jose::JsonWebKeySet; +use mas_jose::jwk::PublicJsonWebKeySet; use serde::{ de::{DeserializeOwned, Error}, ser::SerializeMap, @@ -99,7 +99,7 @@ pub struct ClientMetadataSerdeHelper { application_type: Option, contacts: Option>, jwks_uri: Option, - jwks: Option, + jwks: Option, sector_identifier_uri: Option, subject_type: Option, token_endpoint_auth_method: Option, diff --git a/crates/oauth2-types/src/registration/mod.rs b/crates/oauth2-types/src/registration/mod.rs index 99075320..b1011128 100644 --- a/crates/oauth2-types/src/registration/mod.rs +++ b/crates/oauth2-types/src/registration/mod.rs @@ -20,7 +20,7 @@ use mas_iana::{ jose::{JsonWebEncryptionAlg, JsonWebEncryptionEnc, JsonWebSignatureAlg}, oauth::{OAuthAuthorizationEndpointResponseType, OAuthClientAuthenticationMethod}, }; -use mas_jose::JsonWebKeySet; +use mas_jose::jwk::PublicJsonWebKeySet; use serde::{Deserialize, Serialize}; use serde_with::{serde_as, skip_serializing_none, TimestampSeconds}; use thiserror::Error; @@ -197,7 +197,7 @@ pub struct ClientMetadata { /// This field is mutually exclusive with `jwks_uri`. /// /// [JWK]: https://www.rfc-editor.org/rfc/rfc7517.html - pub jwks: Option, + pub jwks: Option, /// URL to be used in calculating pseudonymous identifiers by the OpenID /// Connect provider when [pairwise subject identifiers] are used. @@ -861,7 +861,7 @@ mod tests { jose::{JsonWebEncryptionAlg, JsonWebEncryptionEnc, JsonWebSignatureAlg}, oauth::{OAuthAuthorizationEndpointResponseType, OAuthClientAuthenticationMethod}, }; - use mas_jose::JsonWebKeySet; + use mas_jose::jwk::PublicJsonWebKeySet; use url::Url; use super::{ClientMetadata, ClientMetadataVerificationError}; @@ -874,7 +874,7 @@ mod tests { } } - fn jwks() -> JsonWebKeySet { + fn jwks() -> PublicJsonWebKeySet { serde_json::from_value(serde_json::json!({ "keys": [ { diff --git a/crates/storage/src/oauth2/client.rs b/crates/storage/src/oauth2/client.rs index f46dcbd7..992bcb46 100644 --- a/crates/storage/src/oauth2/client.rs +++ b/crates/storage/src/oauth2/client.rs @@ -19,7 +19,7 @@ use mas_iana::{ jose::JsonWebSignatureAlg, oauth::{OAuthAuthorizationEndpointResponseType, OAuthClientAuthenticationMethod}, }; -use mas_jose::JsonWebKeySet; +use mas_jose::jwk::PublicJsonWebKeySet; use oauth2_types::requests::GrantType; use sqlx::{PgConnection, PgExecutor}; use thiserror::Error; @@ -331,7 +331,7 @@ pub async fn insert_client( policy_uri: Option<&Url>, tos_uri: Option<&Url>, jwks_uri: Option<&Url>, - jwks: Option<&JsonWebKeySet>, + jwks: Option<&PublicJsonWebKeySet>, id_token_signed_response_alg: Option, userinfo_signed_response_alg: Option, token_endpoint_auth_method: Option, @@ -421,7 +421,7 @@ pub async fn insert_client_from_config( client_id: &str, client_auth_method: OAuthClientAuthenticationMethod, encrypted_client_secret: Option<&str>, - jwks: Option<&JsonWebKeySet>, + jwks: Option<&PublicJsonWebKeySet>, jwks_uri: Option<&Url>, redirect_uris: &[Url], ) -> anyhow::Result<()> {