diff --git a/Cargo.lock b/Cargo.lock index 77b1edbe..aa490fac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2519,7 +2519,7 @@ version = "0.1.0" dependencies = [ "parse-display", "schemars", - "serde", + "serde_with", ] [[package]] diff --git a/crates/axum-utils/src/client_authorization.rs b/crates/axum-utils/src/client_authorization.rs index e6379688..4b9de2a2 100644 --- a/crates/axum-utils/src/client_authorization.rs +++ b/crates/axum-utils/src/client_authorization.rs @@ -92,7 +92,7 @@ impl Credentials { pub async fn verify( &self, encrypter: &Encrypter, - method: OAuthClientAuthenticationMethod, + method: &OAuthClientAuthenticationMethod, client: &Client, ) -> Result<(), CredentialsVerificationError> { match (self, method) { diff --git a/crates/handlers/src/oauth2/introspection.rs b/crates/handlers/src/oauth2/introspection.rs index 9d6b11db..edbd01d9 100644 --- a/crates/handlers/src/oauth2/introspection.rs +++ b/crates/handlers/src/oauth2/introspection.rs @@ -162,7 +162,7 @@ pub(crate) async fn post( let client = client_authorization.credentials.fetch(&mut conn).await?; - let method = match client.token_endpoint_auth_method { + let method = match &client.token_endpoint_auth_method { None | Some(OAuthClientAuthenticationMethod::None) => { return Err(RouteError::NotAllowed); } diff --git a/crates/handlers/src/oauth2/registration.rs b/crates/handlers/src/oauth2/registration.rs index 9d8cf3a9..a2f42f00 100644 --- a/crates/handlers/src/oauth2/registration.rs +++ b/crates/handlers/src/oauth2/registration.rs @@ -151,10 +151,10 @@ pub(crate) async fn post( metadata.tos_uri.as_ref().map(Localized::non_localized), metadata.jwks_uri.as_ref(), metadata.jwks.as_ref(), - metadata.id_token_signed_response_alg, - metadata.userinfo_signed_response_alg, - metadata.token_endpoint_auth_method, - metadata.token_endpoint_auth_signing_alg, + metadata.id_token_signed_response_alg.as_ref(), + metadata.userinfo_signed_response_alg.as_ref(), + metadata.token_endpoint_auth_method.as_ref(), + metadata.token_endpoint_auth_signing_alg.as_ref(), metadata.initiate_login_uri.as_ref(), ) .await?; diff --git a/crates/handlers/src/oauth2/token.rs b/crates/handlers/src/oauth2/token.rs index a91166b6..7209a61a 100644 --- a/crates/handlers/src/oauth2/token.rs +++ b/crates/handlers/src/oauth2/token.rs @@ -198,6 +198,7 @@ pub(crate) async fn post( let method = client .token_endpoint_auth_method + .as_ref() .ok_or(RouteError::ClientNotAllowed)?; client_authorization @@ -333,17 +334,18 @@ async fn authorization_code_grant( let alg = client .id_token_signed_response_alg + .clone() .unwrap_or(JsonWebSignatureAlg::Rs256); let key = key_store - .signing_key_for_algorithm(alg) + .signing_key_for_algorithm(&alg) .context("no suitable key found")?; - claims::AT_HASH.insert(&mut claims, hash_token(alg, &access_token_str)?)?; - claims::C_HASH.insert(&mut claims, hash_token(alg, &grant.code)?)?; + claims::AT_HASH.insert(&mut claims, hash_token(&alg, &access_token_str)?)?; + claims::C_HASH.insert(&mut claims, hash_token(&alg, &grant.code)?)?; + let signer = key.params().signing_key_for_alg(&alg)?; let header = JsonWebSignatureHeader::new(alg) .with_kid(key.kid().context("key has no `kid` for some reason")?); - let signer = key.params().signing_key_for_alg(alg)?; let id_token = Jwt::sign(header, claims, &signer)?; Some(id_token.as_str().to_owned()) diff --git a/crates/handlers/src/oauth2/userinfo.rs b/crates/handlers/src/oauth2/userinfo.rs index aa1130f1..a2c1bf69 100644 --- a/crates/handlers/src/oauth2/userinfo.rs +++ b/crates/handlers/src/oauth2/userinfo.rs @@ -75,12 +75,12 @@ pub async fn get( if let Some(alg) = session.client.userinfo_signed_response_alg { let key = key_store - .signing_key_for_algorithm(alg) + .signing_key_for_algorithm(&alg) .context("no suitable key found")?; + let signer = key.params().signing_key_for_alg(&alg)?; let header = JsonWebSignatureHeader::new(alg) .with_kid(key.kid().context("key has no `kid` for some reason")?); - let signer = key.params().signing_key_for_alg(alg)?; let user_info = SignedUserInfo { iss: url_builder.oidc_issuer().to_string(), diff --git a/crates/iana-codegen/src/main.rs b/crates/iana-codegen/src/main.rs index 91789e21..e140a4ff 100644 --- a/crates/iana-codegen/src/main.rs +++ b/crates/iana-codegen/src/main.rs @@ -105,7 +105,7 @@ impl Display for File { use parse_display::{{Display, FromStr}}; use schemars::JsonSchema; -use serde::{{Deserialize, Serialize}};"#, +use serde_with::{{DeserializeFromStr, SerializeDisplay}};"#, self.registry_name, self.registry_url, )?; @@ -116,6 +116,14 @@ use serde::{{Deserialize, Serialize}};"#, continue; }; + let is_exhaustive = section.key == "OAuthAuthorizationEndpointResponseType"; + + let non_exhaustive_attr = if is_exhaustive { + "" + } else { + "\n#[non_exhaustive]" + }; + write!( f, r#" @@ -125,7 +133,6 @@ use serde::{{Deserialize, Serialize}};"#, #[derive( Debug, Clone, - Copy, PartialEq, Eq, PartialOrd, @@ -133,13 +140,14 @@ use serde::{{Deserialize, Serialize}};"#, Hash, Display, FromStr, - Serialize, - Deserialize, + SerializeDisplay, + DeserializeFromStr, JsonSchema, -)] +)]{} pub enum {} {{"#, section.doc, section.url.unwrap(), + non_exhaustive_attr, section.key, )?; for member in list { @@ -149,10 +157,20 @@ pub enum {} {{"#, } else { writeln!(f, " /// `{}`", member.value)?; } - writeln!(f, " #[serde(rename = \"{}\")]", member.value)?; + writeln!(f, " #[schemars(rename = \"{}\")]", member.value)?; writeln!(f, " #[display(\"{}\")]", member.value)?; writeln!(f, " {},", member.enum_name)?; } + + if !is_exhaustive { + // Add a variant for custom enums + writeln!(f)?; + writeln!(f, " /// An unknown value.")?; + writeln!(f, " #[display(\"{{0}}\")]")?; + writeln!(f, " #[schemars(skip)]")?; + writeln!(f, " Unknown(String),")?; + } + writeln!(f, "}}")?; } diff --git a/crates/iana/Cargo.toml b/crates/iana/Cargo.toml index c1cbc3f9..665a06ab 100644 --- a/crates/iana/Cargo.toml +++ b/crates/iana/Cargo.toml @@ -6,6 +6,6 @@ edition = "2021" license = "Apache-2.0" [dependencies] -serde = "1.0.145" +serde_with = "2.0.0" schemars = "0.8.10" parse-display = "0.6.0" diff --git a/crates/iana/src/jose.rs b/crates/iana/src/jose.rs index 43e9bf41..cc4a16c2 100644 --- a/crates/iana/src/jose.rs +++ b/crates/iana/src/jose.rs @@ -19,7 +19,7 @@ use parse_display::{Display, FromStr}; use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; +use serde_with::{DeserializeFromStr, SerializeDisplay}; /// JSON Web Signature "alg" parameter /// @@ -27,7 +27,6 @@ use serde::{Deserialize, Serialize}; #[derive( Debug, Clone, - Copy, PartialEq, Eq, PartialOrd, @@ -35,85 +34,91 @@ use serde::{Deserialize, Serialize}; Hash, Display, FromStr, - Serialize, - Deserialize, + SerializeDisplay, + DeserializeFromStr, JsonSchema, )] +#[non_exhaustive] pub enum JsonWebSignatureAlg { /// HMAC using SHA-256 - #[serde(rename = "HS256")] + #[schemars(rename = "HS256")] #[display("HS256")] Hs256, /// HMAC using SHA-384 - #[serde(rename = "HS384")] + #[schemars(rename = "HS384")] #[display("HS384")] Hs384, /// HMAC using SHA-512 - #[serde(rename = "HS512")] + #[schemars(rename = "HS512")] #[display("HS512")] Hs512, /// RSASSA-PKCS1-v1_5 using SHA-256 - #[serde(rename = "RS256")] + #[schemars(rename = "RS256")] #[display("RS256")] Rs256, /// RSASSA-PKCS1-v1_5 using SHA-384 - #[serde(rename = "RS384")] + #[schemars(rename = "RS384")] #[display("RS384")] Rs384, /// RSASSA-PKCS1-v1_5 using SHA-512 - #[serde(rename = "RS512")] + #[schemars(rename = "RS512")] #[display("RS512")] Rs512, /// ECDSA using P-256 and SHA-256 - #[serde(rename = "ES256")] + #[schemars(rename = "ES256")] #[display("ES256")] Es256, /// ECDSA using P-384 and SHA-384 - #[serde(rename = "ES384")] + #[schemars(rename = "ES384")] #[display("ES384")] Es384, /// ECDSA using P-521 and SHA-512 - #[serde(rename = "ES512")] + #[schemars(rename = "ES512")] #[display("ES512")] Es512, /// RSASSA-PSS using SHA-256 and MGF1 with SHA-256 - #[serde(rename = "PS256")] + #[schemars(rename = "PS256")] #[display("PS256")] Ps256, /// RSASSA-PSS using SHA-384 and MGF1 with SHA-384 - #[serde(rename = "PS384")] + #[schemars(rename = "PS384")] #[display("PS384")] Ps384, /// RSASSA-PSS using SHA-512 and MGF1 with SHA-512 - #[serde(rename = "PS512")] + #[schemars(rename = "PS512")] #[display("PS512")] Ps512, /// No digital signature or MAC performed - #[serde(rename = "none")] + #[schemars(rename = "none")] #[display("none")] None, /// EdDSA signature algorithms - #[serde(rename = "EdDSA")] + #[schemars(rename = "EdDSA")] #[display("EdDSA")] EdDsa, /// ECDSA using secp256k1 curve and SHA-256 - #[serde(rename = "ES256K")] + #[schemars(rename = "ES256K")] #[display("ES256K")] Es256K, + + /// An unknown value. + #[display("{0}")] + #[schemars(skip)] + Unknown(String), } /// JSON Web Encryption "alg" parameter @@ -122,7 +127,6 @@ pub enum JsonWebSignatureAlg { #[derive( Debug, Clone, - Copy, PartialEq, Eq, PartialOrd, @@ -130,105 +134,111 @@ pub enum JsonWebSignatureAlg { Hash, Display, FromStr, - Serialize, - Deserialize, + SerializeDisplay, + DeserializeFromStr, JsonSchema, )] +#[non_exhaustive] pub enum JsonWebEncryptionAlg { /// RSAES-PKCS1-v1_5 - #[serde(rename = "RSA1_5")] + #[schemars(rename = "RSA1_5")] #[display("RSA1_5")] Rsa15, /// RSAES OAEP using default parameters - #[serde(rename = "RSA-OAEP")] + #[schemars(rename = "RSA-OAEP")] #[display("RSA-OAEP")] RsaOaep, /// RSAES OAEP using SHA-256 and MGF1 with SHA-256 - #[serde(rename = "RSA-OAEP-256")] + #[schemars(rename = "RSA-OAEP-256")] #[display("RSA-OAEP-256")] RsaOaep256, /// AES Key Wrap using 128-bit key - #[serde(rename = "A128KW")] + #[schemars(rename = "A128KW")] #[display("A128KW")] A128Kw, /// AES Key Wrap using 192-bit key - #[serde(rename = "A192KW")] + #[schemars(rename = "A192KW")] #[display("A192KW")] A192Kw, /// AES Key Wrap using 256-bit key - #[serde(rename = "A256KW")] + #[schemars(rename = "A256KW")] #[display("A256KW")] A256Kw, /// Direct use of a shared symmetric key - #[serde(rename = "dir")] + #[schemars(rename = "dir")] #[display("dir")] Dir, /// ECDH-ES using Concat KDF - #[serde(rename = "ECDH-ES")] + #[schemars(rename = "ECDH-ES")] #[display("ECDH-ES")] EcdhEs, /// ECDH-ES using Concat KDF and "A128KW" wrapping - #[serde(rename = "ECDH-ES+A128KW")] + #[schemars(rename = "ECDH-ES+A128KW")] #[display("ECDH-ES+A128KW")] EcdhEsA128Kw, /// ECDH-ES using Concat KDF and "A192KW" wrapping - #[serde(rename = "ECDH-ES+A192KW")] + #[schemars(rename = "ECDH-ES+A192KW")] #[display("ECDH-ES+A192KW")] EcdhEsA192Kw, /// ECDH-ES using Concat KDF and "A256KW" wrapping - #[serde(rename = "ECDH-ES+A256KW")] + #[schemars(rename = "ECDH-ES+A256KW")] #[display("ECDH-ES+A256KW")] EcdhEsA256Kw, /// Key wrapping with AES GCM using 128-bit key - #[serde(rename = "A128GCMKW")] + #[schemars(rename = "A128GCMKW")] #[display("A128GCMKW")] A128Gcmkw, /// Key wrapping with AES GCM using 192-bit key - #[serde(rename = "A192GCMKW")] + #[schemars(rename = "A192GCMKW")] #[display("A192GCMKW")] A192Gcmkw, /// Key wrapping with AES GCM using 256-bit key - #[serde(rename = "A256GCMKW")] + #[schemars(rename = "A256GCMKW")] #[display("A256GCMKW")] A256Gcmkw, /// PBES2 with HMAC SHA-256 and "A128KW" wrapping - #[serde(rename = "PBES2-HS256+A128KW")] + #[schemars(rename = "PBES2-HS256+A128KW")] #[display("PBES2-HS256+A128KW")] Pbes2Hs256A128Kw, /// PBES2 with HMAC SHA-384 and "A192KW" wrapping - #[serde(rename = "PBES2-HS384+A192KW")] + #[schemars(rename = "PBES2-HS384+A192KW")] #[display("PBES2-HS384+A192KW")] Pbes2Hs384A192Kw, /// PBES2 with HMAC SHA-512 and "A256KW" wrapping - #[serde(rename = "PBES2-HS512+A256KW")] + #[schemars(rename = "PBES2-HS512+A256KW")] #[display("PBES2-HS512+A256KW")] Pbes2Hs512A256Kw, /// RSA-OAEP using SHA-384 and MGF1 with SHA-384 - #[serde(rename = "RSA-OAEP-384")] + #[schemars(rename = "RSA-OAEP-384")] #[display("RSA-OAEP-384")] RsaOaep384, /// RSA-OAEP using SHA-512 and MGF1 with SHA-512 - #[serde(rename = "RSA-OAEP-512")] + #[schemars(rename = "RSA-OAEP-512")] #[display("RSA-OAEP-512")] RsaOaep512, + + /// An unknown value. + #[display("{0}")] + #[schemars(skip)] + Unknown(String), } /// JSON Web Encryption "enc" parameter @@ -237,7 +247,6 @@ pub enum JsonWebEncryptionAlg { #[derive( Debug, Clone, - Copy, PartialEq, Eq, PartialOrd, @@ -245,40 +254,46 @@ pub enum JsonWebEncryptionAlg { Hash, Display, FromStr, - Serialize, - Deserialize, + SerializeDisplay, + DeserializeFromStr, JsonSchema, )] +#[non_exhaustive] pub enum JsonWebEncryptionEnc { /// AES_128_CBC_HMAC_SHA_256 authenticated encryption algorithm - #[serde(rename = "A128CBC-HS256")] + #[schemars(rename = "A128CBC-HS256")] #[display("A128CBC-HS256")] A128CbcHs256, /// AES_192_CBC_HMAC_SHA_384 authenticated encryption algorithm - #[serde(rename = "A192CBC-HS384")] + #[schemars(rename = "A192CBC-HS384")] #[display("A192CBC-HS384")] A192CbcHs384, /// AES_256_CBC_HMAC_SHA_512 authenticated encryption algorithm - #[serde(rename = "A256CBC-HS512")] + #[schemars(rename = "A256CBC-HS512")] #[display("A256CBC-HS512")] A256CbcHs512, /// AES GCM using 128-bit key - #[serde(rename = "A128GCM")] + #[schemars(rename = "A128GCM")] #[display("A128GCM")] A128Gcm, /// AES GCM using 192-bit key - #[serde(rename = "A192GCM")] + #[schemars(rename = "A192GCM")] #[display("A192GCM")] A192Gcm, /// AES GCM using 256-bit key - #[serde(rename = "A256GCM")] + #[schemars(rename = "A256GCM")] #[display("A256GCM")] A256Gcm, + + /// An unknown value. + #[display("{0}")] + #[schemars(skip)] + Unknown(String), } /// JSON Web Encryption Compression Algorithm @@ -287,7 +302,6 @@ pub enum JsonWebEncryptionEnc { #[derive( Debug, Clone, - Copy, PartialEq, Eq, PartialOrd, @@ -295,15 +309,21 @@ pub enum JsonWebEncryptionEnc { Hash, Display, FromStr, - Serialize, - Deserialize, + SerializeDisplay, + DeserializeFromStr, JsonSchema, )] +#[non_exhaustive] pub enum JsonWebEncryptionCompressionAlgorithm { /// DEFLATE - #[serde(rename = "DEF")] + #[schemars(rename = "DEF")] #[display("DEF")] Def, + + /// An unknown value. + #[display("{0}")] + #[schemars(skip)] + Unknown(String), } /// JSON Web Key Type @@ -312,7 +332,6 @@ pub enum JsonWebEncryptionCompressionAlgorithm { #[derive( Debug, Clone, - Copy, PartialEq, Eq, PartialOrd, @@ -320,30 +339,36 @@ pub enum JsonWebEncryptionCompressionAlgorithm { Hash, Display, FromStr, - Serialize, - Deserialize, + SerializeDisplay, + DeserializeFromStr, JsonSchema, )] +#[non_exhaustive] pub enum JsonWebKeyType { /// Elliptic Curve - #[serde(rename = "EC")] + #[schemars(rename = "EC")] #[display("EC")] Ec, /// RSA - #[serde(rename = "RSA")] + #[schemars(rename = "RSA")] #[display("RSA")] Rsa, /// Octet sequence - #[serde(rename = "oct")] + #[schemars(rename = "oct")] #[display("oct")] Oct, /// Octet string key pairs - #[serde(rename = "OKP")] + #[schemars(rename = "OKP")] #[display("OKP")] Okp, + + /// An unknown value. + #[display("{0}")] + #[schemars(skip)] + Unknown(String), } /// JSON Web Key EC Elliptic Curve @@ -352,7 +377,6 @@ pub enum JsonWebKeyType { #[derive( Debug, Clone, - Copy, PartialEq, Eq, PartialOrd, @@ -360,30 +384,36 @@ pub enum JsonWebKeyType { Hash, Display, FromStr, - Serialize, - Deserialize, + SerializeDisplay, + DeserializeFromStr, JsonSchema, )] +#[non_exhaustive] pub enum JsonWebKeyEcEllipticCurve { /// P-256 Curve - #[serde(rename = "P-256")] + #[schemars(rename = "P-256")] #[display("P-256")] P256, /// P-384 Curve - #[serde(rename = "P-384")] + #[schemars(rename = "P-384")] #[display("P-384")] P384, /// P-521 Curve - #[serde(rename = "P-521")] + #[schemars(rename = "P-521")] #[display("P-521")] P521, /// SECG secp256k1 curve - #[serde(rename = "secp256k1")] + #[schemars(rename = "secp256k1")] #[display("secp256k1")] Secp256K1, + + /// An unknown value. + #[display("{0}")] + #[schemars(skip)] + Unknown(String), } /// JSON Web Key OKP Elliptic Curve @@ -392,7 +422,6 @@ pub enum JsonWebKeyEcEllipticCurve { #[derive( Debug, Clone, - Copy, PartialEq, Eq, PartialOrd, @@ -400,30 +429,36 @@ pub enum JsonWebKeyEcEllipticCurve { Hash, Display, FromStr, - Serialize, - Deserialize, + SerializeDisplay, + DeserializeFromStr, JsonSchema, )] +#[non_exhaustive] pub enum JsonWebKeyOkpEllipticCurve { /// Ed25519 signature algorithm key pairs - #[serde(rename = "Ed25519")] + #[schemars(rename = "Ed25519")] #[display("Ed25519")] Ed25519, /// Ed448 signature algorithm key pairs - #[serde(rename = "Ed448")] + #[schemars(rename = "Ed448")] #[display("Ed448")] Ed448, /// X25519 function key pairs - #[serde(rename = "X25519")] + #[schemars(rename = "X25519")] #[display("X25519")] X25519, /// X448 function key pairs - #[serde(rename = "X448")] + #[schemars(rename = "X448")] #[display("X448")] X448, + + /// An unknown value. + #[display("{0}")] + #[schemars(skip)] + Unknown(String), } /// JSON Web Key Use @@ -432,7 +467,6 @@ pub enum JsonWebKeyOkpEllipticCurve { #[derive( Debug, Clone, - Copy, PartialEq, Eq, PartialOrd, @@ -440,20 +474,26 @@ pub enum JsonWebKeyOkpEllipticCurve { Hash, Display, FromStr, - Serialize, - Deserialize, + SerializeDisplay, + DeserializeFromStr, JsonSchema, )] +#[non_exhaustive] pub enum JsonWebKeyUse { /// Digital Signature or MAC - #[serde(rename = "sig")] + #[schemars(rename = "sig")] #[display("sig")] Sig, /// Encryption - #[serde(rename = "enc")] + #[schemars(rename = "enc")] #[display("enc")] Enc, + + /// An unknown value. + #[display("{0}")] + #[schemars(skip)] + Unknown(String), } /// JSON Web Key Operation @@ -462,7 +502,6 @@ pub enum JsonWebKeyUse { #[derive( Debug, Clone, - Copy, PartialEq, Eq, PartialOrd, @@ -470,48 +509,54 @@ pub enum JsonWebKeyUse { Hash, Display, FromStr, - Serialize, - Deserialize, + SerializeDisplay, + DeserializeFromStr, JsonSchema, )] +#[non_exhaustive] pub enum JsonWebKeyOperation { /// Compute digital signature or MAC - #[serde(rename = "sign")] + #[schemars(rename = "sign")] #[display("sign")] Sign, /// Verify digital signature or MAC - #[serde(rename = "verify")] + #[schemars(rename = "verify")] #[display("verify")] Verify, /// Encrypt content - #[serde(rename = "encrypt")] + #[schemars(rename = "encrypt")] #[display("encrypt")] Encrypt, /// Decrypt content and validate decryption, if applicable - #[serde(rename = "decrypt")] + #[schemars(rename = "decrypt")] #[display("decrypt")] Decrypt, /// Encrypt key - #[serde(rename = "wrapKey")] + #[schemars(rename = "wrapKey")] #[display("wrapKey")] WrapKey, /// Decrypt key and validate decryption, if applicable - #[serde(rename = "unwrapKey")] + #[schemars(rename = "unwrapKey")] #[display("unwrapKey")] UnwrapKey, /// Derive key - #[serde(rename = "deriveKey")] + #[schemars(rename = "deriveKey")] #[display("deriveKey")] DeriveKey, /// Derive bits not to be used as a key - #[serde(rename = "deriveBits")] + #[schemars(rename = "deriveBits")] #[display("deriveBits")] DeriveBits, + + /// An unknown value. + #[display("{0}")] + #[schemars(skip)] + Unknown(String), } diff --git a/crates/iana/src/oauth.rs b/crates/iana/src/oauth.rs index 66999d81..4014571c 100644 --- a/crates/iana/src/oauth.rs +++ b/crates/iana/src/oauth.rs @@ -19,7 +19,7 @@ use parse_display::{Display, FromStr}; use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; +use serde_with::{DeserializeFromStr, SerializeDisplay}; /// OAuth Access Token Type /// @@ -27,7 +27,6 @@ use serde::{Deserialize, Serialize}; #[derive( Debug, Clone, - Copy, PartialEq, Eq, PartialOrd, @@ -35,25 +34,31 @@ use serde::{Deserialize, Serialize}; Hash, Display, FromStr, - Serialize, - Deserialize, + SerializeDisplay, + DeserializeFromStr, JsonSchema, )] +#[non_exhaustive] pub enum OAuthAccessTokenType { /// `Bearer` - #[serde(rename = "Bearer")] + #[schemars(rename = "Bearer")] #[display("Bearer")] Bearer, /// `N_A` - #[serde(rename = "N_A")] + #[schemars(rename = "N_A")] #[display("N_A")] Na, /// `PoP` - #[serde(rename = "PoP")] + #[schemars(rename = "PoP")] #[display("PoP")] PoP, + + /// An unknown value. + #[display("{0}")] + #[schemars(skip)] + Unknown(String), } /// OAuth Authorization Endpoint Response Type @@ -62,7 +67,6 @@ pub enum OAuthAccessTokenType { #[derive( Debug, Clone, - Copy, PartialEq, Eq, PartialOrd, @@ -70,48 +74,48 @@ pub enum OAuthAccessTokenType { Hash, Display, FromStr, - Serialize, - Deserialize, + SerializeDisplay, + DeserializeFromStr, JsonSchema, )] pub enum OAuthAuthorizationEndpointResponseType { /// `code` - #[serde(rename = "code")] + #[schemars(rename = "code")] #[display("code")] Code, /// `code id_token` - #[serde(rename = "code id_token")] + #[schemars(rename = "code id_token")] #[display("code id_token")] CodeIdToken, /// `code id_token token` - #[serde(rename = "code id_token token")] + #[schemars(rename = "code id_token token")] #[display("code id_token token")] CodeIdTokenToken, /// `code token` - #[serde(rename = "code token")] + #[schemars(rename = "code token")] #[display("code token")] CodeToken, /// `id_token` - #[serde(rename = "id_token")] + #[schemars(rename = "id_token")] #[display("id_token")] IdToken, /// `id_token token` - #[serde(rename = "id_token token")] + #[schemars(rename = "id_token token")] #[display("id_token token")] IdTokenToken, /// `none` - #[serde(rename = "none")] + #[schemars(rename = "none")] #[display("none")] None, /// `token` - #[serde(rename = "token")] + #[schemars(rename = "token")] #[display("token")] Token, } @@ -122,7 +126,6 @@ pub enum OAuthAuthorizationEndpointResponseType { #[derive( Debug, Clone, - Copy, PartialEq, Eq, PartialOrd, @@ -130,25 +133,31 @@ pub enum OAuthAuthorizationEndpointResponseType { Hash, Display, FromStr, - Serialize, - Deserialize, + SerializeDisplay, + DeserializeFromStr, JsonSchema, )] +#[non_exhaustive] pub enum OAuthTokenTypeHint { /// `access_token` - #[serde(rename = "access_token")] + #[schemars(rename = "access_token")] #[display("access_token")] AccessToken, /// `refresh_token` - #[serde(rename = "refresh_token")] + #[schemars(rename = "refresh_token")] #[display("refresh_token")] RefreshToken, /// `pct` - #[serde(rename = "pct")] + #[schemars(rename = "pct")] #[display("pct")] Pct, + + /// An unknown value. + #[display("{0}")] + #[schemars(skip)] + Unknown(String), } /// OAuth Token Endpoint Authentication Method @@ -157,7 +166,6 @@ pub enum OAuthTokenTypeHint { #[derive( Debug, Clone, - Copy, PartialEq, Eq, PartialOrd, @@ -165,45 +173,51 @@ pub enum OAuthTokenTypeHint { Hash, Display, FromStr, - Serialize, - Deserialize, + SerializeDisplay, + DeserializeFromStr, JsonSchema, )] +#[non_exhaustive] pub enum OAuthClientAuthenticationMethod { /// `none` - #[serde(rename = "none")] + #[schemars(rename = "none")] #[display("none")] None, /// `client_secret_post` - #[serde(rename = "client_secret_post")] + #[schemars(rename = "client_secret_post")] #[display("client_secret_post")] ClientSecretPost, /// `client_secret_basic` - #[serde(rename = "client_secret_basic")] + #[schemars(rename = "client_secret_basic")] #[display("client_secret_basic")] ClientSecretBasic, /// `client_secret_jwt` - #[serde(rename = "client_secret_jwt")] + #[schemars(rename = "client_secret_jwt")] #[display("client_secret_jwt")] ClientSecretJwt, /// `private_key_jwt` - #[serde(rename = "private_key_jwt")] + #[schemars(rename = "private_key_jwt")] #[display("private_key_jwt")] PrivateKeyJwt, /// `tls_client_auth` - #[serde(rename = "tls_client_auth")] + #[schemars(rename = "tls_client_auth")] #[display("tls_client_auth")] TlsClientAuth, /// `self_signed_tls_client_auth` - #[serde(rename = "self_signed_tls_client_auth")] + #[schemars(rename = "self_signed_tls_client_auth")] #[display("self_signed_tls_client_auth")] SelfSignedTlsClientAuth, + + /// An unknown value. + #[display("{0}")] + #[schemars(skip)] + Unknown(String), } /// PKCE Code Challenge Method @@ -212,7 +226,6 @@ pub enum OAuthClientAuthenticationMethod { #[derive( Debug, Clone, - Copy, PartialEq, Eq, PartialOrd, @@ -220,18 +233,24 @@ pub enum OAuthClientAuthenticationMethod { Hash, Display, FromStr, - Serialize, - Deserialize, + SerializeDisplay, + DeserializeFromStr, JsonSchema, )] +#[non_exhaustive] pub enum PkceCodeChallengeMethod { /// `plain` - #[serde(rename = "plain")] + #[schemars(rename = "plain")] #[display("plain")] Plain, /// `S256` - #[serde(rename = "S256")] + #[schemars(rename = "S256")] #[display("S256")] S256, + + /// An unknown value. + #[display("{0}")] + #[schemars(skip)] + Unknown(String), } diff --git a/crates/jose/src/claims.rs b/crates/jose/src/claims.rs index 1a0bec5c..557e73d3 100644 --- a/crates/jose/src/claims.rs +++ b/crates/jose/src/claims.rs @@ -248,7 +248,7 @@ impl From<&TimeOptions> for TimeNotBefore { /// Returns an error if the algorithm is not supported. /// /// [OpenID Connect Core 1.0 specification]: https://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken -pub fn hash_token(alg: JsonWebSignatureAlg, token: &str) -> anyhow::Result { +pub fn hash_token(alg: &JsonWebSignatureAlg, token: &str) -> anyhow::Result { let bits = match alg { JsonWebSignatureAlg::Hs256 | JsonWebSignatureAlg::Rs256 @@ -281,9 +281,7 @@ pub fn hash_token(alg: JsonWebSignatureAlg, token: &str) -> anyhow::Result { - return Err(anyhow::anyhow!("unsupported algorithm for hashing")) - } + _ => return Err(anyhow::anyhow!("unsupported algorithm for hashing")), } .context("failed to get first half of hash")?; @@ -292,14 +290,14 @@ pub fn hash_token(alg: JsonWebSignatureAlg, token: &str) -> anyhow::Result { - alg: JsonWebSignatureAlg, + alg: &'a JsonWebSignatureAlg, token: &'a str, } impl<'a> TokenHash<'a> { /// Creates a new `TokenHash` validator for the given algorithm and token. #[must_use] - pub fn new(alg: JsonWebSignatureAlg, token: &'a str) -> Self { + pub fn new(alg: &'a JsonWebSignatureAlg, token: &'a str) -> Self { Self { alg, token } } } diff --git a/crates/jose/src/constraints.rs b/crates/jose/src/constraints.rs index 51dfcbe9..1246289d 100644 --- a/crates/jose/src/constraints.rs +++ b/crates/jose/src/constraints.rs @@ -21,7 +21,7 @@ use crate::jwt::JsonWebSignatureHeader; #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Constraint<'a> { Alg { - constraint_alg: JsonWebSignatureAlg, + constraint_alg: &'a JsonWebSignatureAlg, }, Algs { @@ -33,17 +33,17 @@ pub enum Constraint<'a> { }, Use { - constraint_use: JsonWebKeyUse, + constraint_use: &'a JsonWebKeyUse, }, Kty { - constraint_kty: JsonWebKeyType, + constraint_kty: &'a JsonWebKeyType, }, } impl<'a> Constraint<'a> { #[must_use] - pub fn alg(constraint_alg: JsonWebSignatureAlg) -> Self { + pub fn alg(constraint_alg: &'a JsonWebSignatureAlg) -> Self { Constraint::Alg { constraint_alg } } @@ -58,12 +58,12 @@ impl<'a> Constraint<'a> { } #[must_use] - pub fn use_(constraint_use: JsonWebKeyUse) -> Self { + pub fn use_(constraint_use: &'a JsonWebKeyUse) -> Self { Constraint::Use { constraint_use } } #[must_use] - pub fn kty(constraint_kty: JsonWebKeyType) -> Self { + pub fn kty(constraint_kty: &'a JsonWebKeyType) -> Self { Constraint::Kty { constraint_kty } } } @@ -76,7 +76,7 @@ pub enum ConstraintDecision { } pub trait Constrainable { - fn alg(&self) -> Option { + fn alg(&self) -> Option<&JsonWebSignatureAlg> { None } @@ -91,7 +91,7 @@ pub trait Constrainable { } /// Usage specified for this key - fn use_(&self) -> Option { + fn use_(&self) -> Option<&JsonWebKeyUse> { None } @@ -120,7 +120,7 @@ impl<'a> Constraint<'a> { } Constraint::Algs { constraint_algs } => { if let Some(alg) = constrainable.alg() { - if constraint_algs.contains(&alg) { + if constraint_algs.contains(alg) { ConstraintDecision::Positive } else { ConstraintDecision::Negative @@ -158,7 +158,7 @@ impl<'a> Constraint<'a> { } } Constraint::Kty { constraint_kty } => { - if *constraint_kty == constrainable.kty() { + if **constraint_kty == constrainable.kty() { ConstraintDecision::Positive } else { ConstraintDecision::Negative @@ -217,7 +217,7 @@ impl<'a> ConstraintSet<'a> { } #[must_use] - pub fn alg(mut self, constraint_alg: JsonWebSignatureAlg) -> Self { + pub fn alg(mut self, constraint_alg: &'a JsonWebSignatureAlg) -> Self { self.constraints.insert(Constraint::alg(constraint_alg)); self } @@ -235,13 +235,13 @@ impl<'a> ConstraintSet<'a> { } #[must_use] - pub fn use_(mut self, constraint_use: JsonWebKeyUse) -> Self { + pub fn use_(mut self, constraint_use: &'a JsonWebKeyUse) -> Self { self.constraints.insert(Constraint::use_(constraint_use)); self } #[must_use] - pub fn kty(mut self, constraint_kty: JsonWebKeyType) -> Self { + pub fn kty(mut self, constraint_kty: &'a JsonWebKeyType) -> Self { self.constraints.insert(Constraint::kty(constraint_kty)); self } diff --git a/crates/jose/src/jwa/asymmetric.rs b/crates/jose/src/jwa/asymmetric.rs index 88ebc091..a2d5d776 100644 --- a/crates/jose/src/jwa/asymmetric.rs +++ b/crates/jose/src/jwa/asymmetric.rs @@ -59,7 +59,7 @@ impl AsymmetricSigningKey { #[allow(dead_code)] pub fn from_jwk_and_alg( params: &JsonWebKeyPrivateParameters, - alg: JsonWebSignatureAlg, + alg: &JsonWebSignatureAlg, ) -> Result { match (params, alg) { (JsonWebKeyPrivateParameters::Rsa(params), alg) => match alg { @@ -69,7 +69,7 @@ impl AsymmetricSigningKey { JsonWebSignatureAlg::Ps256 => Ok(Self::Ps256(params.try_into()?)), JsonWebSignatureAlg::Ps384 => Ok(Self::Ps384(params.try_into()?)), JsonWebSignatureAlg::Ps512 => Ok(Self::Ps512(params.try_into()?)), - _ => Err(AsymmetricKeyFromJwkError::KeyNotSuitable { alg }), + _ => Err(AsymmetricKeyFromJwkError::KeyNotSuitable { alg: alg.clone() }), }, (JsonWebKeyPrivateParameters::Ec(params), JsonWebSignatureAlg::Es256) @@ -87,7 +87,7 @@ impl AsymmetricSigningKey { (JsonWebKeyPrivateParameters::Ec(params), JsonWebSignatureAlg::Es512) if params.crv == JsonWebKeyEcEllipticCurve::P521 => { - Err(AsymmetricKeyFromJwkError::UnsupportedAlgorithm { alg }) + Err(AsymmetricKeyFromJwkError::UnsupportedAlgorithm { alg: alg.clone() }) } (JsonWebKeyPrivateParameters::Ec(params), JsonWebSignatureAlg::Es256K) @@ -97,10 +97,10 @@ impl AsymmetricSigningKey { } (JsonWebKeyPrivateParameters::Okp(_params), JsonWebSignatureAlg::EdDsa) => { - Err(AsymmetricKeyFromJwkError::UnsupportedAlgorithm { alg }) + Err(AsymmetricKeyFromJwkError::UnsupportedAlgorithm { alg: alg.clone() }) } - _ => Err(AsymmetricKeyFromJwkError::KeyNotSuitable { alg }), + _ => Err(AsymmetricKeyFromJwkError::KeyNotSuitable { alg: alg.clone() }), } } } @@ -219,7 +219,7 @@ pub enum AsymmetricVerifyingKey { impl AsymmetricVerifyingKey { pub fn from_jwk_and_alg( params: &JsonWebKeyPublicParameters, - alg: JsonWebSignatureAlg, + alg: &JsonWebSignatureAlg, ) -> Result { match (params, alg) { (JsonWebKeyPublicParameters::Rsa(params), alg) => match alg { @@ -229,7 +229,7 @@ impl AsymmetricVerifyingKey { JsonWebSignatureAlg::Ps256 => Ok(Self::Ps256(params.try_into()?)), JsonWebSignatureAlg::Ps384 => Ok(Self::Ps384(params.try_into()?)), JsonWebSignatureAlg::Ps512 => Ok(Self::Ps512(params.try_into()?)), - _ => Err(AsymmetricKeyFromJwkError::KeyNotSuitable { alg }), + _ => Err(AsymmetricKeyFromJwkError::KeyNotSuitable { alg: alg.clone() }), }, (JsonWebKeyPublicParameters::Ec(params), JsonWebSignatureAlg::Es256) @@ -247,7 +247,7 @@ impl AsymmetricVerifyingKey { (JsonWebKeyPublicParameters::Ec(params), JsonWebSignatureAlg::Es512) if params.crv == JsonWebKeyEcEllipticCurve::P521 => { - Err(AsymmetricKeyFromJwkError::UnsupportedAlgorithm { alg }) + Err(AsymmetricKeyFromJwkError::UnsupportedAlgorithm { alg: alg.clone() }) } (JsonWebKeyPublicParameters::Ec(params), JsonWebSignatureAlg::Es256K) @@ -257,10 +257,10 @@ impl AsymmetricVerifyingKey { } (JsonWebKeyPublicParameters::Okp(_params), JsonWebSignatureAlg::EdDsa) => { - Err(AsymmetricKeyFromJwkError::UnsupportedAlgorithm { alg }) + Err(AsymmetricKeyFromJwkError::UnsupportedAlgorithm { alg: alg.clone() }) } - _ => Err(AsymmetricKeyFromJwkError::KeyNotSuitable { alg }), + _ => Err(AsymmetricKeyFromJwkError::KeyNotSuitable { alg: alg.clone() }), } } } diff --git a/crates/jose/src/jwa/symmetric.rs b/crates/jose/src/jwa/symmetric.rs index 2e879041..66663587 100644 --- a/crates/jose/src/jwa/symmetric.rs +++ b/crates/jose/src/jwa/symmetric.rs @@ -33,15 +33,15 @@ pub struct InvalidAlgorithm { } impl SymmetricKey { - pub const fn new_for_alg( - key: Vec, - alg: JsonWebSignatureAlg, - ) -> Result { + pub fn new_for_alg(key: Vec, alg: &JsonWebSignatureAlg) -> Result { match alg { JsonWebSignatureAlg::Hs256 => Ok(Self::hs256(key)), JsonWebSignatureAlg::Hs384 => Ok(Self::hs384(key)), JsonWebSignatureAlg::Hs512 => Ok(Self::hs512(key)), - _ => Err(InvalidAlgorithm { alg, key }), + _ => Err(InvalidAlgorithm { + alg: alg.clone(), + key, + }), } } diff --git a/crates/jose/src/jwk/mod.rs b/crates/jose/src/jwk/mod.rs index 9466b000..07420e54 100644 --- a/crates/jose/src/jwk/mod.rs +++ b/crates/jose/src/jwk/mod.rs @@ -166,9 +166,9 @@ impl

JsonWebKey

{ { Ok(JsonWebKey { parameters: mapper(&self.parameters)?, - r#use: self.r#use, + r#use: self.r#use.clone(), key_ops: self.key_ops.clone(), - alg: self.alg, + alg: self.alg.clone(), kid: self.kid.clone(), x5u: self.x5u.clone(), x5c: self.x5c.clone(), @@ -183,9 +183,9 @@ impl

JsonWebKey

{ { JsonWebKey { parameters: mapper(&self.parameters), - r#use: self.r#use, + r#use: self.r#use.clone(), key_ops: self.key_ops.clone(), - alg: self.alg, + alg: self.alg.clone(), kid: self.kid.clone(), x5u: self.x5u.clone(), x5c: self.x5c.clone(), @@ -195,7 +195,7 @@ impl

JsonWebKey

{ } #[must_use] - pub const fn with_use(mut self, value: JsonWebKeyUse) -> Self { + pub fn with_use(mut self, value: JsonWebKeyUse) -> Self { self.r#use = Some(value); self } @@ -207,7 +207,7 @@ impl

JsonWebKey

{ } #[must_use] - pub const fn with_alg(mut self, alg: JsonWebSignatureAlg) -> Self { + pub fn with_alg(mut self, alg: JsonWebSignatureAlg) -> Self { self.alg = Some(alg); self } @@ -219,8 +219,8 @@ impl

JsonWebKey

{ } #[must_use] - pub const fn alg(&self) -> Option { - self.alg + pub const fn alg(&self) -> Option<&JsonWebSignatureAlg> { + self.alg.as_ref() } #[must_use] @@ -245,12 +245,12 @@ where self.parameters.possible_algs() } - fn alg(&self) -> Option { - self.alg + fn alg(&self) -> Option<&JsonWebSignatureAlg> { + self.alg.as_ref() } - fn use_(&self) -> Option { - self.r#use + fn use_(&self) -> Option<&JsonWebKeyUse> { + self.r#use.as_ref() } } @@ -317,13 +317,13 @@ impl

JsonWebKeySet

{ /// Find a key for the given algorithm. Returns `None` if no suitable key /// was found. #[must_use] - pub fn signing_key_for_algorithm(&self, alg: JsonWebSignatureAlg) -> Option<&JsonWebKey

> + pub fn signing_key_for_algorithm(&self, alg: &JsonWebSignatureAlg) -> Option<&JsonWebKey

> where P: ParametersInfo, { let constraints = ConstraintSet::new([ Constraint::alg(alg), - Constraint::use_(mas_iana::jose::JsonWebKeyUse::Sig), + Constraint::use_(&mas_iana::jose::JsonWebKeyUse::Sig), ]); self.find_key(&constraints) } @@ -338,7 +338,7 @@ impl

JsonWebKeySet

{ .keys .iter() .flat_map(|key| key.params().possible_algs()) - .copied() + .cloned() .collect(); algs.sort(); algs.dedup(); @@ -389,15 +389,15 @@ mod tests { } let constraints = ConstraintSet::default() - .use_(JsonWebKeyUse::Sig) - .kty(JsonWebKeyType::Rsa) - .alg(JsonWebSignatureAlg::Rs256); + .use_(&JsonWebKeyUse::Sig) + .kty(&JsonWebKeyType::Rsa) + .alg(&JsonWebSignatureAlg::Rs256); let candidates = constraints.filter(&jwks.keys); assert_eq!(candidates.len(), 2); let constraints = ConstraintSet::default() - .use_(JsonWebKeyUse::Sig) - .kty(JsonWebKeyType::Rsa) + .use_(&JsonWebKeyUse::Sig) + .kty(&JsonWebKeyType::Rsa) .kid("03e84aed4ef4431014e8617567864c4efaaaede9"); let candidates = constraints.filter(&jwks.keys); assert_eq!(candidates.len(), 1); diff --git a/crates/jose/src/jwk/private_parameters.rs b/crates/jose/src/jwk/private_parameters.rs index f8729819..11a7c619 100644 --- a/crates/jose/src/jwk/private_parameters.rs +++ b/crates/jose/src/jwk/private_parameters.rs @@ -335,6 +335,7 @@ impl ParametersInfo for EcPrivateParameters { JsonWebKeyEcEllipticCurve::P384 => &[JsonWebSignatureAlg::Es384], JsonWebKeyEcEllipticCurve::P521 => &[JsonWebSignatureAlg::Es512], JsonWebKeyEcEllipticCurve::Secp256K1 => &[JsonWebSignatureAlg::Es256K], + _ => &[], } } } diff --git a/crates/jose/src/jwk/public_parameters.rs b/crates/jose/src/jwk/public_parameters.rs index 674887fe..ef3c58cc 100644 --- a/crates/jose/src/jwk/public_parameters.rs +++ b/crates/jose/src/jwk/public_parameters.rs @@ -137,8 +137,8 @@ impl EcPublicParameters { Self { crv, x, y } } - pub const fn crv(&self) -> JsonWebKeyEcEllipticCurve { - self.crv + pub const fn crv(&self) -> &JsonWebKeyEcEllipticCurve { + &self.crv } } @@ -148,11 +148,12 @@ impl ParametersInfo for EcPublicParameters { } fn possible_algs(&self) -> &[JsonWebSignatureAlg] { - match self.crv { + match &self.crv { JsonWebKeyEcEllipticCurve::P256 => &[JsonWebSignatureAlg::Es256], JsonWebKeyEcEllipticCurve::P384 => &[JsonWebSignatureAlg::Es384], JsonWebKeyEcEllipticCurve::P521 => &[JsonWebSignatureAlg::Es512], JsonWebKeyEcEllipticCurve::Secp256K1 => &[JsonWebSignatureAlg::Es256K], + _ => &[], } } } @@ -182,8 +183,8 @@ impl OkpPublicParameters { Self { crv, x } } - pub const fn crv(&self) -> JsonWebKeyOkpEllipticCurve { - self.crv + pub const fn crv(&self) -> &JsonWebKeyOkpEllipticCurve { + &self.crv } } @@ -360,7 +361,7 @@ mod ec_impls { type Error = ecdsa::Error; fn try_from(value: &EcPublicParameters) -> Result { - if value.crv() != C::CRV { + if *value.crv() != C::CRV { return Err(Self::Error::default()); } diff --git a/crates/jose/src/jwt/header.rs b/crates/jose/src/jwt/header.rs index ef03454e..b5a0b49e 100644 --- a/crates/jose/src/jwt/header.rs +++ b/crates/jose/src/jwt/header.rs @@ -82,8 +82,8 @@ impl JsonWebSignatureHeader { } #[must_use] - pub const fn alg(&self) -> JsonWebSignatureAlg { - self.alg + pub const fn alg(&self) -> &JsonWebSignatureAlg { + &self.alg } #[must_use] diff --git a/crates/jose/tests/jws.rs b/crates/jose/tests/jws.rs index b88c2d87..c408052b 100644 --- a/crates/jose/tests/jws.rs +++ b/crates/jose/tests/jws.rs @@ -70,7 +70,7 @@ macro_rules! asymetric_jwt_test { fn validate_jwt() { let jwt: Jwt<'_, Payload> = Jwt::try_from($jwt).unwrap(); assert_eq!(jwt.payload().hello, "world"); - assert_eq!(jwt.header().alg(), JsonWebSignatureAlg::$alg); + assert_eq!(*jwt.header().alg(), JsonWebSignatureAlg::$alg); } #[test] @@ -105,7 +105,7 @@ macro_rules! asymetric_jwt_test { let key = mas_jose::jwa::AsymmetricVerifyingKey::from_jwk_and_alg( key.params(), - JsonWebSignatureAlg::$alg, + &JsonWebSignatureAlg::$alg, ) .unwrap(); @@ -118,12 +118,12 @@ macro_rules! asymetric_jwt_test { let payload = Payload { hello: "world".to_string(), }; - let header = JsonWebSignatureHeader::new(alg); + let header = JsonWebSignatureHeader::new(alg.clone()); let jwks = private_jwks(); - let key = jwks.signing_key_for_algorithm(alg).unwrap(); + let key = jwks.signing_key_for_algorithm(&alg).unwrap(); - let key = mas_jose::jwa::AsymmetricSigningKey::from_jwk_and_alg(key.params(), alg) + let key = mas_jose::jwa::AsymmetricSigningKey::from_jwk_and_alg(key.params(), &alg) .unwrap(); let jwt: Jwt<'_, Payload> = Jwt::sign(header, payload, &key).unwrap(); @@ -133,7 +133,7 @@ macro_rules! asymetric_jwt_test { let key = jwks.find_key(&jwt.header().into()).unwrap(); let key = - mas_jose::jwa::AsymmetricVerifyingKey::from_jwk_and_alg(key.params(), alg) + mas_jose::jwa::AsymmetricVerifyingKey::from_jwk_and_alg(key.params(), &alg) .unwrap(); jwt.verify(&key).unwrap(); @@ -155,14 +155,14 @@ macro_rules! symetric_jwt_test { fn validate_jwt() { let jwt: Jwt<'_, Payload> = Jwt::try_from($jwt).unwrap(); assert_eq!(jwt.payload().hello, "world"); - assert_eq!(jwt.header().alg(), JsonWebSignatureAlg::$alg); + assert_eq!(*jwt.header().alg(), JsonWebSignatureAlg::$alg); } #[test] fn verify_jwt() { let jwt: Jwt<'_, Payload> = Jwt::try_from($jwt).unwrap(); let key = - mas_jose::jwa::SymmetricKey::new_for_alg(oct_key(), JsonWebSignatureAlg::$alg) + mas_jose::jwa::SymmetricKey::new_for_alg(oct_key(), &JsonWebSignatureAlg::$alg) .unwrap(); jwt.verify(&key).unwrap(); } @@ -173,9 +173,9 @@ macro_rules! symetric_jwt_test { let payload = Payload { hello: "world".to_string(), }; - let header = JsonWebSignatureHeader::new(alg); + let header = JsonWebSignatureHeader::new(alg.clone()); - let key = mas_jose::jwa::SymmetricKey::new_for_alg(oct_key(), alg).unwrap(); + let key = mas_jose::jwa::SymmetricKey::new_for_alg(oct_key(), &alg).unwrap(); let jwt: Jwt<'_, Payload> = Jwt::sign(header, payload, &key).unwrap(); let jwt: Jwt<'_, Payload> = Jwt::try_from(jwt.as_str()).unwrap(); diff --git a/crates/keystore/src/lib.rs b/crates/keystore/src/lib.rs index 25fc257f..235768ac 100644 --- a/crates/keystore/src/lib.rs +++ b/crates/keystore/src/lib.rs @@ -415,7 +415,7 @@ impl PrivateKey { /// Returns an error if the key is not suited for the selected algorithm pub fn verifying_key_for_alg( &self, - alg: JsonWebSignatureAlg, + alg: &JsonWebSignatureAlg, ) -> Result { let key = match (self, alg) { (Self::Rsa(key), _) => { @@ -469,7 +469,7 @@ impl PrivateKey { /// Returns an error if the key is not suited for the selected algorithm pub fn signing_key_for_alg( &self, - alg: JsonWebSignatureAlg, + alg: &JsonWebSignatureAlg, ) -> Result { let key = match (self, alg) { (Self::Rsa(key), _) => { diff --git a/crates/keystore/tests/keystore.rs b/crates/keystore/tests/keystore.rs index 1779c440..c089fe9e 100644 --- a/crates/keystore/tests/keystore.rs +++ b/crates/keystore/tests/keystore.rs @@ -33,8 +33,8 @@ macro_rules! plain_test { let algs = key.possible_algs(); assert_ne!(algs.len(), 0); - for &alg in algs { - let header = JsonWebSignatureHeader::new(alg); + for alg in algs { + let header = JsonWebSignatureHeader::new(alg.clone()); let payload = "hello"; let signer = key.signing_key_for_alg(alg).unwrap(); let jwt = Jwt::sign(header, payload, &signer).unwrap(); @@ -58,8 +58,8 @@ macro_rules! enc_test { let algs = key.possible_algs(); assert_ne!(algs.len(), 0); - for &alg in algs { - let header = JsonWebSignatureHeader::new(alg); + for alg in algs { + let header = JsonWebSignatureHeader::new(alg.clone()); let payload = "hello"; let signer = key.signing_key_for_alg(alg).unwrap(); let jwt = Jwt::sign(header, payload, &signer).unwrap(); diff --git a/crates/oauth2-types/src/oidc.rs b/crates/oauth2-types/src/oidc.rs index 3a43f84f..d5c29f0e 100644 --- a/crates/oauth2-types/src/oidc.rs +++ b/crates/oauth2-types/src/oidc.rs @@ -33,7 +33,7 @@ use crate::{ /// An enum for types that accept either an [`OAuthClientAuthenticationMethod`] /// or an [`OAuthAccessTokenType`]. #[derive( - SerializeDisplay, DeserializeFromStr, Clone, Copy, PartialEq, Eq, Hash, Debug, Display, FromStr, + SerializeDisplay, DeserializeFromStr, Clone, PartialEq, Eq, Hash, Debug, Display, FromStr, )] pub enum AuthenticationMethodOrAccessTokenType { /// An authentication method. @@ -49,9 +49,9 @@ impl AuthenticationMethodOrAccessTokenType { /// Get the authentication method of this /// `AuthenticationMethodOrAccessTokenType`. #[must_use] - pub fn authentication_method(&self) -> Option { + pub fn authentication_method(&self) -> Option<&OAuthClientAuthenticationMethod> { match self { - Self::AuthenticationMethod(m) => Some(*m), + Self::AuthenticationMethod(m) => Some(m), Self::AccessTokenType(_) => None, } } @@ -59,10 +59,10 @@ impl AuthenticationMethodOrAccessTokenType { /// Get the access token type of this /// `AuthenticationMethodOrAccessTokenType`. #[must_use] - pub fn access_token_type(&self) -> Option { + pub fn access_token_type(&self) -> Option<&OAuthAccessTokenType> { match self { Self::AuthenticationMethod(_) => None, - Self::AccessTokenType(t) => Some(*t), + Self::AccessTokenType(t) => Some(t), } } } @@ -470,8 +470,14 @@ impl ProviderMetadata { validate_signing_alg_values_supported( "token_endpoint", - &metadata.token_endpoint_auth_signing_alg_values_supported, - &metadata.token_endpoint_auth_methods_supported, + metadata + .token_endpoint_auth_signing_alg_values_supported + .iter() + .flatten(), + metadata + .token_endpoint_auth_methods_supported + .iter() + .flatten(), )?; if let Some(url) = &metadata.revocation_endpoint { @@ -480,8 +486,14 @@ impl ProviderMetadata { validate_signing_alg_values_supported( "revocation_endpoint", - &metadata.revocation_endpoint_auth_signing_alg_values_supported, - &metadata.revocation_endpoint_auth_methods_supported, + metadata + .revocation_endpoint_auth_signing_alg_values_supported + .iter() + .flatten(), + metadata + .revocation_endpoint_auth_methods_supported + .iter() + .flatten(), )?; if let Some(url) = &metadata.introspection_endpoint { @@ -500,8 +512,11 @@ impl ProviderMetadata { }); validate_signing_alg_values_supported( "introspection_endpoint", - &metadata.introspection_endpoint_auth_signing_alg_values_supported, - &introspection_methods, + metadata + .introspection_endpoint_auth_signing_alg_values_supported + .iter() + .flatten(), + introspection_methods.into_iter().flatten(), )?; if let Some(url) = &metadata.userinfo_endpoint { @@ -957,24 +972,32 @@ fn validate_url( /// - The algorithm values must not contain `none`, /// - If the `client_secret_jwt` or `private_key_jwt` authentication methods are /// supported, the values must be present. -fn validate_signing_alg_values_supported( +fn validate_signing_alg_values_supported<'a>( endpoint: &'static str, - values: &Option>, - methods: &Option>, + values: impl Iterator, + mut methods: impl Iterator, ) -> Result<(), ProviderMetadataVerificationError> { - if let Some(values) = values { - if values.contains(&JsonWebSignatureAlg::None) { + let mut no_values = true; + + for value in values { + if *value == JsonWebSignatureAlg::None { return Err(ProviderMetadataVerificationError::SigningAlgValuesWithNone( endpoint, )); } - } else if methods.iter().flatten().any(|method| { - matches!( - method, - OAuthClientAuthenticationMethod::ClientSecretJwt - | OAuthClientAuthenticationMethod::PrivateKeyJwt - ) - }) { + + no_values = false; + } + + if no_values + && methods.any(|method| { + matches!( + method, + OAuthClientAuthenticationMethod::ClientSecretJwt + | OAuthClientAuthenticationMethod::PrivateKeyJwt + ) + }) + { return Err(ProviderMetadataVerificationError::MissingAuthSigningAlgValues(endpoint)); } diff --git a/crates/oauth2-types/src/pkce.rs b/crates/oauth2-types/src/pkce.rs index 97321ec5..ccd47c52 100644 --- a/crates/oauth2-types/src/pkce.rs +++ b/crates/oauth2-types/src/pkce.rs @@ -33,6 +33,9 @@ pub enum CodeChallengeError { #[error("challenge verification failed")] VerificationFailed, + + #[error("unknown challenge method")] + UnknownChallengeMethod, } fn validate_verifier(verifier: &str) -> Result<(), CodeChallengeError> { @@ -61,7 +64,7 @@ pub trait CodeChallengeMethodExt { /// /// Returns an error if the verifier did not adhere to the rules defined by /// the RFC in terms of length and allowed characters - fn compute_challenge(self, verifier: &str) -> Result, CodeChallengeError>; + fn compute_challenge<'a>(&self, verifier: &'a str) -> Result, CodeChallengeError>; /// Verify that a given verifier is valid for the given challenge /// @@ -70,7 +73,7 @@ pub trait CodeChallengeMethodExt { /// Returns an error if the verifier did not match the challenge, or if the /// verifier did not adhere to the rules defined by the RFC in terms of /// length and allowed characters - fn verify(self, challenge: &str, verifier: &str) -> Result<(), CodeChallengeError> + fn verify(&self, challenge: &str, verifier: &str) -> Result<(), CodeChallengeError> where Self: Sized, { @@ -83,7 +86,7 @@ pub trait CodeChallengeMethodExt { } impl CodeChallengeMethodExt for PkceCodeChallengeMethod { - fn compute_challenge(self, verifier: &str) -> Result, CodeChallengeError> { + fn compute_challenge<'a>(&self, verifier: &'a str) -> Result, CodeChallengeError> { validate_verifier(verifier)?; let challenge = match self { @@ -95,6 +98,7 @@ impl CodeChallengeMethodExt for PkceCodeChallengeMethod { let verifier = BASE64URL_NOPAD.encode(&hash); verifier.into() } + _ => return Err(CodeChallengeError::UnknownChallengeMethod), }; Ok(challenge) diff --git a/crates/oauth2-types/src/registration/mod.rs b/crates/oauth2-types/src/registration/mod.rs index 895b71e0..d2436d19 100644 --- a/crates/oauth2-types/src/registration/mod.rs +++ b/crates/oauth2-types/src/registration/mod.rs @@ -42,13 +42,13 @@ pub const DEFAULT_GRANT_TYPES: &[GrantType] = &[GrantType::AuthorizationCode]; pub const DEFAULT_APPLICATION_TYPE: ApplicationType = ApplicationType::Web; -pub const DEFAULT_TOKEN_AUTH_METHOD: OAuthClientAuthenticationMethod = - OAuthClientAuthenticationMethod::ClientSecretBasic; +pub const DEFAULT_TOKEN_AUTH_METHOD: &OAuthClientAuthenticationMethod = + &OAuthClientAuthenticationMethod::ClientSecretBasic; -pub const DEFAULT_SIGNING_ALGORITHM: JsonWebSignatureAlg = JsonWebSignatureAlg::Rs256; +pub const DEFAULT_SIGNING_ALGORITHM: &JsonWebSignatureAlg = &JsonWebSignatureAlg::Rs256; -pub const DEFAULT_ENCRYPTION_ENC_ALGORITHM: JsonWebEncryptionEnc = - JsonWebEncryptionEnc::A128CbcHs256; +pub const DEFAULT_ENCRYPTION_ENC_ALGORITHM: &JsonWebEncryptionEnc = + &JsonWebEncryptionEnc::A128CbcHs256; /// A collection of localized variants. /// @@ -463,7 +463,7 @@ impl ClientMetadata { )); } - if self.token_endpoint_auth_method() == OAuthClientAuthenticationMethod::PrivateKeyJwt + if *self.token_endpoint_auth_method() == OAuthClientAuthenticationMethod::PrivateKeyJwt && self.jwks_uri.is_none() && self.jwks.is_none() { @@ -486,26 +486,26 @@ impl ClientMetadata { )); } - if self.id_token_signed_response_alg() == JsonWebSignatureAlg::None + if *self.id_token_signed_response_alg() == JsonWebSignatureAlg::None && response_types.iter().any(ResponseType::has_id_token) { return Err(ClientMetadataVerificationError::IdTokenSigningAlgNone); } if self.id_token_encrypted_response_enc.is_some() { - self.id_token_encrypted_response_alg.ok_or( + self.id_token_encrypted_response_alg.as_ref().ok_or( ClientMetadataVerificationError::MissingEncryptionAlg("id_token"), )?; } if self.userinfo_encrypted_response_enc.is_some() { - self.userinfo_encrypted_response_alg.ok_or( + self.userinfo_encrypted_response_alg.as_ref().ok_or( ClientMetadataVerificationError::MissingEncryptionAlg("userinfo"), )?; } if self.request_object_encryption_enc.is_some() { - self.request_object_encryption_alg.ok_or( + self.request_object_encryption_alg.as_ref().ok_or( ClientMetadataVerificationError::MissingEncryptionAlg("request_object"), )?; } @@ -522,7 +522,7 @@ impl ClientMetadata { } if self.introspection_encrypted_response_enc.is_some() { - self.introspection_encrypted_response_alg.ok_or( + self.introspection_encrypted_response_alg.as_ref().ok_or( ClientMetadataVerificationError::MissingEncryptionAlg("introspection"), )?; } @@ -542,9 +542,12 @@ impl ClientMetadata { /// [authorization endpoint]: https://www.rfc-editor.org/rfc/rfc6749.html#section-3.1 #[must_use] pub fn response_types(&self) -> Vec { - self.response_types - .clone() - .unwrap_or_else(|| DEFAULT_RESPONSE_TYPES.map(ResponseType::from).into()) + self.response_types.clone().unwrap_or_else(|| { + DEFAULT_RESPONSE_TYPES + .into_iter() + .filter_map(|t| ResponseType::try_from(t).ok()) + .collect() + }) } /// Array of [OAuth 2.0 `grant_type` values] that the client can use at the @@ -578,8 +581,9 @@ impl ClientMetadata { /// /// [token endpoint]: https://www.rfc-editor.org/rfc/rfc6749.html#section-3.2 #[must_use] - pub fn token_endpoint_auth_method(&self) -> OAuthClientAuthenticationMethod { + pub fn token_endpoint_auth_method(&self) -> &OAuthClientAuthenticationMethod { self.token_endpoint_auth_method + .as_ref() .unwrap_or(DEFAULT_TOKEN_AUTH_METHOD) } @@ -594,8 +598,9 @@ impl ClientMetadata { /// /// [JWS]: http://tools.ietf.org/html/draft-ietf-jose-json-web-signature #[must_use] - pub fn id_token_signed_response_alg(&self) -> JsonWebSignatureAlg { + pub fn id_token_signed_response_alg(&self) -> &JsonWebSignatureAlg { self.id_token_signed_response_alg + .as_ref() .unwrap_or(DEFAULT_SIGNING_ALGORITHM) } @@ -610,11 +615,12 @@ impl ClientMetadata { #[must_use] pub fn id_token_encrypted_response( &self, - ) -> Option<(JsonWebEncryptionAlg, JsonWebEncryptionEnc)> { - self.id_token_encrypted_response_alg.map(|alg| { + ) -> Option<(&JsonWebEncryptionAlg, &JsonWebEncryptionEnc)> { + self.id_token_encrypted_response_alg.as_ref().map(|alg| { ( alg, self.id_token_encrypted_response_enc + .as_ref() .unwrap_or(DEFAULT_ENCRYPTION_ENC_ALGORITHM), ) }) @@ -631,11 +637,12 @@ impl ClientMetadata { #[must_use] pub fn userinfo_encrypted_response( &self, - ) -> Option<(JsonWebEncryptionAlg, JsonWebEncryptionEnc)> { - self.userinfo_encrypted_response_alg.map(|alg| { + ) -> Option<(&JsonWebEncryptionAlg, &JsonWebEncryptionEnc)> { + self.userinfo_encrypted_response_alg.as_ref().map(|alg| { ( alg, self.userinfo_encrypted_response_enc + .as_ref() .unwrap_or(DEFAULT_ENCRYPTION_ENC_ALGORITHM), ) }) @@ -652,11 +659,12 @@ impl ClientMetadata { #[must_use] pub fn request_object_encryption( &self, - ) -> Option<(JsonWebEncryptionAlg, JsonWebEncryptionEnc)> { - self.request_object_encryption_alg.map(|alg| { + ) -> Option<(&JsonWebEncryptionAlg, &JsonWebEncryptionEnc)> { + self.request_object_encryption_alg.as_ref().map(|alg| { ( alg, self.request_object_encryption_enc + .as_ref() .unwrap_or(DEFAULT_ENCRYPTION_ENC_ALGORITHM), ) }) @@ -705,14 +713,17 @@ impl ClientMetadata { #[must_use] pub fn introspection_encrypted_response( &self, - ) -> Option<(JsonWebEncryptionAlg, JsonWebEncryptionEnc)> { - self.introspection_encrypted_response_alg.map(|alg| { - ( - alg, - self.introspection_encrypted_response_enc - .unwrap_or(DEFAULT_ENCRYPTION_ENC_ALGORITHM), - ) - }) + ) -> Option<(&JsonWebEncryptionAlg, &JsonWebEncryptionEnc)> { + self.introspection_encrypted_response_alg + .as_ref() + .map(|alg| { + ( + alg, + self.introspection_encrypted_response_enc + .as_ref() + .unwrap_or(DEFAULT_ENCRYPTION_ENC_ALGORITHM), + ) + }) } } diff --git a/crates/storage/src/oauth2/client.rs b/crates/storage/src/oauth2/client.rs index 9357a7ab..bafe461a 100644 --- a/crates/storage/src/oauth2/client.rs +++ b/crates/storage/src/oauth2/client.rs @@ -332,10 +332,10 @@ pub async fn insert_client( tos_uri: Option<&Url>, jwks_uri: Option<&Url>, jwks: Option<&PublicJsonWebKeySet>, - id_token_signed_response_alg: Option, - userinfo_signed_response_alg: Option, - token_endpoint_auth_method: Option, - token_endpoint_auth_signing_alg: Option, + id_token_signed_response_alg: Option<&JsonWebSignatureAlg>, + userinfo_signed_response_alg: Option<&JsonWebSignatureAlg>, + token_endpoint_auth_method: Option<&OAuthClientAuthenticationMethod>, + token_endpoint_auth_signing_alg: Option<&JsonWebSignatureAlg>, initiate_login_uri: Option<&Url>, ) -> Result<(), sqlx::Error> { let response_types: Vec = response_types.iter().map(ToString::to_string).collect(); @@ -347,10 +347,10 @@ pub async fn insert_client( let tos_uri = tos_uri.map(Url::as_str); let jwks = jwks.map(serde_json::to_value).transpose().unwrap(); // TODO let jwks_uri = jwks_uri.map(Url::as_str); - let id_token_signed_response_alg = id_token_signed_response_alg.map(|v| v.to_string()); - let userinfo_signed_response_alg = userinfo_signed_response_alg.map(|v| v.to_string()); - let token_endpoint_auth_method = token_endpoint_auth_method.map(|v| v.to_string()); - let token_endpoint_auth_signing_alg = token_endpoint_auth_signing_alg.map(|v| v.to_string()); + let id_token_signed_response_alg = id_token_signed_response_alg.map(ToString::to_string); + let userinfo_signed_response_alg = userinfo_signed_response_alg.map(ToString::to_string); + let token_endpoint_auth_method = token_endpoint_auth_method.map(ToString::to_string); + let token_endpoint_auth_signing_alg = token_endpoint_auth_signing_alg.map(ToString::to_string); let initiate_login_uri = initiate_login_uri.map(Url::as_str); let id = sqlx::query_scalar!(