refactor: use typed Vec<Jwk> in JWKS parsing, remove double deserialize

This commit is contained in:
Dionisio
2026-02-15 18:19:11 +01:00
parent b59ff75489
commit 1527223d62
+11 -18
View File
@@ -32,7 +32,7 @@ struct OidcDiscovery {
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
struct JwksDocument { struct JwksDocument {
keys: Vec<serde_json::Value>, keys: Vec<jsonwebtoken::jwk::Jwk>,
} }
// ============================================================================ // ============================================================================
@@ -95,7 +95,7 @@ pub struct OidcService {
http_client: reqwest::Client, http_client: reqwest::Client,
/// Cached discovery document /// Cached discovery document
discovery: RwLock<Option<OidcDiscovery>>, discovery: RwLock<Option<OidcDiscovery>>,
/// Cached JWKS (raw JSON values) /// Cached JWKS (typed JWK keys)
jwks: RwLock<Option<JwksDocument>>, jwks: RwLock<Option<JwksDocument>>,
} }
@@ -225,16 +225,18 @@ impl OidcService {
} }
/// Find a suitable key from JWKS by kid header (filters out encryption keys) /// Find a suitable key from JWKS by kid header (filters out encryption keys)
fn find_key<'a>(jwks: &'a JwksDocument, kid: Option<&str>) -> Option<&'a serde_json::Value> { fn find_key<'a>(
jwks: &'a JwksDocument,
kid: Option<&str>,
) -> Option<&'a jsonwebtoken::jwk::Jwk> {
jwks.keys.iter().find(|k| { jwks.keys.iter().find(|k| {
// Exclude encryption keys (only use signature keys) // Exclude encryption keys (only use signature keys)
let use_field = k.get("use").and_then(|v| v.as_str()); if k.common.public_key_use == Some(jsonwebtoken::jwk::PublicKeyUse::Encryption) {
if use_field == Some("enc") {
return false; return false;
} }
// Match kid if provided // Match kid if provided
if let Some(target_kid) = kid { if let Some(target_kid) = kid {
return k.get("kid").and_then(|v| v.as_str()) == Some(target_kid); return k.common.key_id.as_deref() == Some(target_kid);
} }
// No kid specified, include this key // No kid specified, include this key
true true
@@ -367,8 +369,8 @@ impl OidcServicePort for OidcService {
// Extract kid from JWT header // Extract kid from JWT header
let kid = Self::extract_jwt_kid(id_token); let kid = Self::extract_jwt_kid(id_token);
// Find the matching key from JWKS // Find the matching key from JWKS (already typed as Jwk)
let jwk_value = Self::find_key(&jwks, kid.as_deref()).ok_or_else(|| { let jwk = Self::find_key(&jwks, kid.as_deref()).ok_or_else(|| {
DomainError::new( DomainError::new(
ErrorKind::AccessDenied, ErrorKind::AccessDenied,
"OIDC", "OIDC",
@@ -376,16 +378,7 @@ impl OidcServicePort for OidcService {
) )
})?; })?;
// Use jsonwebtoken's from_jwk to automatically handle key type and algorithm detection let decoding_key = jsonwebtoken::DecodingKey::from_jwk(jwk).map_err(|e| {
let jwk: jsonwebtoken::jwk::Jwk = serde_json::from_value(jwk_value.clone()).map_err(|e| {
DomainError::new(
ErrorKind::InternalError,
"OIDC",
format!("Failed to parse JWK: {}", e),
)
})?;
let decoding_key = jsonwebtoken::DecodingKey::from_jwk(&jwk).map_err(|e| {
DomainError::new( DomainError::new(
ErrorKind::InternalError, ErrorKind::InternalError,
"OIDC", "OIDC",