diff --git a/jans-cedarling/cedarling/src/init/jwt_algorithm.rs b/jans-cedarling/cedarling/src/init/jwt_algorithm.rs deleted file mode 100644 index e5df42f066e..00000000000 --- a/jans-cedarling/cedarling/src/init/jwt_algorithm.rs +++ /dev/null @@ -1,24 +0,0 @@ -/* - * This software is available under the Apache-2.0 license. - * See https://www.apache.org/licenses/LICENSE-2.0.txt for full text. - * - * Copyright (c) 2024, Gluu, Inc. - */ -use crate::bootstrap_config::BootstrapConfig; -use crate::jwt::{self, string_to_alg, Algorithm}; - -pub(crate) fn parse_jwt_algorithms(config: &BootstrapConfig) -> Result, jwt::Error> { - match &config.jwt_config { - crate::JwtConfig::Disabled => Ok(Vec::new()), - crate::JwtConfig::Enabled { - signature_algorithms, - } => { - let algorithms = signature_algorithms - .iter() - .map(|alg| string_to_alg(alg)) - .collect::, _>>()?; - - Ok(algorithms) - }, - } -} diff --git a/jans-cedarling/cedarling/src/init/mod.rs b/jans-cedarling/cedarling/src/init/mod.rs index d38c9312e7d..f1b588ac515 100644 --- a/jans-cedarling/cedarling/src/init/mod.rs +++ b/jans-cedarling/cedarling/src/init/mod.rs @@ -10,9 +10,7 @@ //! - load Cedar Policies //! - get keys for JWT validation -mod jwt_algorithm; pub(crate) mod policy_store; pub(crate) mod service_config; pub(crate) mod service_factory; - pub(crate) use service_factory::ServiceFactory; diff --git a/jans-cedarling/cedarling/src/init/service_config.rs b/jans-cedarling/cedarling/src/init/service_config.rs index 9cab8db66bd..89bae58ac8a 100644 --- a/jans-cedarling/cedarling/src/init/service_config.rs +++ b/jans-cedarling/cedarling/src/init/service_config.rs @@ -5,24 +5,19 @@ * Copyright (c) 2024, Gluu, Inc. */ -use super::jwt_algorithm::parse_jwt_algorithms; use super::policy_store::{load_policy_store, PolicyStoreLoadError}; +use crate::bootstrap_config; use crate::common::policy_store::PolicyStore; -use crate::{bootstrap_config, jwt}; use bootstrap_config::BootstrapConfig; /// Configuration that hold validated infomation from bootstrap config #[derive(typed_builder::TypedBuilder, Clone)] pub(crate) struct ServiceConfig { pub policy_store: PolicyStore, - pub jwt_algorithms: Vec, } #[derive(thiserror::Error, Debug)] pub enum ServiceConfigError { - /// Parse jwt algorithm error. - #[error("could not parse an algorithim defined in the config: {0}")] - ParseAlgorithm(#[from] jwt::Error), /// Error that may occur during loading the policy store. #[error("Could not load policy: {0}")] PolicyStore(#[from] PolicyStoreLoadError), @@ -31,7 +26,6 @@ pub enum ServiceConfigError { impl ServiceConfig { pub fn new(bootstrap: &BootstrapConfig) -> Result { let builder = ServiceConfig::builder() - .jwt_algorithms(parse_jwt_algorithms(bootstrap)?) .policy_store(load_policy_store(&bootstrap.policy_store_config)?); Ok(builder.build()) diff --git a/jans-cedarling/cedarling/src/init/service_factory.rs b/jans-cedarling/cedarling/src/init/service_factory.rs index 40b99a2af1f..2f379102fa1 100644 --- a/jans-cedarling/cedarling/src/init/service_factory.rs +++ b/jans-cedarling/cedarling/src/init/service_factory.rs @@ -78,17 +78,20 @@ impl<'a> ServiceFactory<'a> { if let Some(jwt_service) = &self.container.jwt_service { jwt_service.clone() } else { - let config = match self.bootstrap_config.jwt_config { + let config = match &self.bootstrap_config.jwt_config { crate::JwtConfig::Disabled => JwtServiceConfig::WithoutValidation, - crate::JwtConfig::Enabled { .. } => JwtServiceConfig::WithValidation { - supported_algs: self.service_config.jwt_algorithms.clone(), + crate::JwtConfig::Enabled { signature_algorithms } => JwtServiceConfig::WithValidation { + supported_algs: signature_algorithms.to_vec(), trusted_idps: self.policy_store().trusted_issuers.expect("Expected trusted issuers to be present for JWT validation, but found None. Ensure that the policy store is properly initialized with trusted issuers before using JWT validation.").clone(), }, }; - - let service = Arc::new(JwtService::new_with_config(config)); - self.container.jwt_service = Some(service.clone()); - service + + // TODO: handle intialization errors + let jwt_service = Arc::new( + JwtService::new_with_config(config).expect("should initialize jwt service"), + ); + self.container.jwt_service = Some(jwt_service.clone()); + jwt_service } } diff --git a/jans-cedarling/cedarling/src/jwt/decoding_strategy.rs b/jans-cedarling/cedarling/src/jwt/decoding_strategy.rs index 80c53a23798..33f8fface76 100644 --- a/jans-cedarling/cedarling/src/jwt/decoding_strategy.rs +++ b/jans-cedarling/cedarling/src/jwt/decoding_strategy.rs @@ -8,9 +8,9 @@ pub mod key_service; use crate::common::policy_store::TrustedIssuer; -use super::Error; +use super::InitError; use jsonwebtoken as jwt; -pub use key_service::*; +pub use key_service::KeyService; use serde::de::DeserializeOwned; /// Represents the decoding strategy for JWT tokens. @@ -47,15 +47,17 @@ impl DecodingStrategy { /// # Errors /// Returns an error if the specified algorithm is unrecognized or the key service initialization fails. pub fn new_with_validation( - config_algs: Vec, + config_algs: Vec, trusted_idps: Vec, - ) -> Result { + ) -> Result { // initialize the key service with OpenID configuration endpoints let openid_conf_endpoints = trusted_idps .iter() .map(|x| x.openid_configuration_endpoint.as_ref()) .collect(); - let key_service = KeyService::new(openid_conf_endpoints).map_err(Error::KeyService)?; + let key_service = KeyService::new(openid_conf_endpoints)?; + + let config_algs = parse_jwt_algorithms(config_algs).map_err(InitError::DecodingStrategy)?; Ok(Self::WithValidation { key_service, @@ -69,7 +71,7 @@ impl DecodingStrategy { iss: Option, aud: Option, req_sub: bool, - ) -> Result { + ) -> Result { match self { DecodingStrategy::WithoutValidation => self.extract_claims(jwt), DecodingStrategy::WithValidation { @@ -86,7 +88,7 @@ impl DecodingStrategy { /// /// # Errors /// Returns an error if the claims cannot be extracted. - pub fn extract_claims(&self, jwt_str: &str) -> Result { + pub fn extract_claims(&self, jwt_str: &str) -> Result { let mut validator = jwt::Validation::default(); validator.insecure_disable_signature_validation(); validator.validate_exp = false; @@ -122,12 +124,12 @@ fn decode_and_validate_jwt( req_sub: bool, supported_algs: &[jwt::Algorithm], key_service: &KeyService, -) -> Result { +) -> Result { let header = jwt::decode_header(jwt).map_err(Error::Parsing)?; // reject unsupported algorithms early if !supported_algs.contains(&header.alg) { - return Err(Error::TokenSignedWithUnsupportedAlgorithm(header.alg)); + return Err(Error::TokenSignedWithUnsupportedAlgorithm(header.alg).into()); } // set up validation rules @@ -149,8 +151,9 @@ fn decode_and_validate_jwt( let kid = &header .kid .ok_or_else(|| Error::MissingRequiredHeader("kid".into()))?; - let key = key_service.get_key(kid).map_err(Error::KeyService)?; - // TODO: handle tokens without a `kid` in the header + let key = key_service.get_key(kid)?; + // TODO: figure out how to handle tokens without a `kid` in the header + // there's no plans yet if we need to support this or not. // decode and validate the jwt let claims = jwt::decode::(jwt, &key, &validator) @@ -159,6 +162,33 @@ fn decode_and_validate_jwt( Ok(claims) } +fn parse_jwt_algorithms(algorithms: Vec) -> Result, Error> { + let parsing_results = algorithms + .iter() + .map(|alg| string_to_alg(alg)) + .collect::>>>(); + + let (successes, errors): (Vec<_>, Vec<_>) = + parsing_results.into_iter().partition(Result::is_ok); + + // Collect all errors into a single error message or return them as a vector. + if !errors.is_empty() { + let unsupported_algs = errors + .into_iter() + .filter_map(Result::err) + .collect::>>() + .join(", "); + return Err(Error::UnimplementedAlgorithm(unsupported_algs)); + } + + let algorithms = successes + .into_iter() + .filter_map(Result::ok) + .collect::>(); + + Ok(algorithms) +} + /// Converts a string representation of an algorithm to a `jwt::Algorithm` enum. /// /// This function maps algorithm names (e.g., "HS256", "RS256") to corresponding @@ -169,7 +199,7 @@ fn decode_and_validate_jwt( /// /// # Errors /// Returns an error if the algorithm is not implemented. -pub fn string_to_alg(algorithm: &str) -> Result { +pub fn string_to_alg(algorithm: &str) -> Result> { match algorithm { "HS256" => Ok(jwt::Algorithm::HS256), "HS384" => Ok(jwt::Algorithm::HS384), @@ -183,6 +213,58 @@ pub fn string_to_alg(algorithm: &str) -> Result { "PS384" => Ok(jwt::Algorithm::PS384), "PS512" => Ok(jwt::Algorithm::PS512), "EdDSA" => Ok(jwt::Algorithm::EdDSA), - _ => Err(Error::UnimplementedAlgorithm(algorithm.into())), + _ => Err(algorithm.into()), } } + +/// Error type for issues encountered during JWT decoding and validation. +/// +/// The `DecodingStrategy` is responsible for parsing, validating, and verifying JWTs. +/// This enum represents various errors that can occur during these processes, such as +/// issues with parsing the token, unsupported algorithms, or validation failures. +#[derive(thiserror::Error, Debug)] +pub enum Error { + /// An error occurred while parsing the JWT. + /// + /// This error occurs when the provided JWT cannot be properly parsed. + /// This might happen if the token is malformed or contains invalid data + /// that does not conform to the JWT structure. + #[error("Error parsing the JWT: {0}")] + Parsing(#[source] jsonwebtoken::errors::Error), + + /// The token was signed using an unsupported algorithm. + /// + /// This error occurs when the JWT's header specifies an algorithm that + /// is not supported by the current validation configuration. Common causes + /// include encountering an algorithm that is disallowed or not yet implemented + /// by the decoding strategy. + #[error("The JWT is signed with an unsupported algorithm: {0:?}")] + TokenSignedWithUnsupportedAlgorithm(jsonwebtoken::Algorithm), + + /// A required header is missing from the JWT. + /// + /// This error occurs when a necessary header is missing from the JWT's header section, + /// preventing proper verification or validation of the token. + /// + /// *Certain headers, such as `kid` (Key ID), are required for proper JWT processing.* + #[error("The JWT is missing a required header: {0}")] + MissingRequiredHeader(String), + + /// JWT validation failed. + /// + /// This error occurs when the JWT fails to pass validation checks. Common causes + /// include an invalid signature, expired tokens, or claims that do not meet + /// the expected criteria. The underlying validation error provides more context + /// on why the token was considered invalid. + #[error("Failed to validate the JWT: {0}")] + Validation(#[source] jsonwebtoken::errors::Error), + + /// The configuration defines an unsupported or unimplemented algorithm. + /// + /// This error occurs when the validation configuration specifies an algorithm + /// that is either not implemented by the `DecodingStrategy` or not recognized + /// by the JWT service. This typically happens when the application tries to use + /// a newer or less common algorithm that has not been added to the supported set. + #[error("An algorithm(s) defined in the configuration is not yet implemented: {0}")] + UnimplementedAlgorithm(String), +} diff --git a/jans-cedarling/cedarling/src/jwt/decoding_strategy/key_service.rs b/jans-cedarling/cedarling/src/jwt/decoding_strategy/key_service.rs index 201ab9c673b..148a2f6931f 100644 --- a/jans-cedarling/cedarling/src/jwt/decoding_strategy/key_service.rs +++ b/jans-cedarling/cedarling/src/jwt/decoding_strategy/key_service.rs @@ -5,16 +5,59 @@ * Copyright (c) 2024, Gluu, Inc. */ -mod error; -mod openid_config; - -pub use error::Error; +use super::super::InitError; use jsonwebtoken::jwk::JwkSet; use jsonwebtoken::DecodingKey; -use openid_config::*; use reqwest::blocking::Client; +use serde::Deserialize; use std::collections::HashMap; use std::sync::Arc; +use std::sync::RwLock; + +/// represents the source data for OpenID configuration. +#[derive(Deserialize)] +pub struct OpenIdConfigSource { + issuer: Box, + jwks_uri: Box, + // The following values are also normally returned when sending + // a GET request to the `openid_configuration_endpoint` but are + // not currently being used. + // + // authorization_endpoint: Box, + // device_authorization_endpoint: Box, + // token_endpoint: Box, + // userinfo_endpoint: Box, + // revocation_endpoint: Box, + // response_types_supported: Vec>, + // subject_types_supported: Vec>, + // id_token_signing_algs_values_supported: Vec>, + // scopes_supported: Vec>, + // claims_supported: Vec>, +} + +/// represents the OpenID configuration for an identity provider. +pub struct OpenIdConfig { + pub jwks_uri: Box, + // + pub decoding_keys: Arc, Arc>>>, +} + +impl OpenIdConfig { + /// creates an `OpenIdConfig` from the provided source. + /// + /// this method extracts the issuer and constructs a new `OpenIdConfig` + /// instance, initializing the decoding keys storage. + pub fn from_source(src: OpenIdConfigSource) -> (Box, OpenIdConfig) { + let issuer = src.issuer; + ( + issuer, + OpenIdConfig { + jwks_uri: src.jwks_uri, + decoding_keys: Arc::new(RwLock::new(HashMap::new())), + }, + ) + } +} pub struct KeyService { idp_configs: HashMap, OpenIdConfig>, // @@ -28,20 +71,22 @@ impl KeyService { /// this method fetches the OpenID configuration and the associated keys (JWKS) for each /// endpoint, populating the internal `idp_configs` map. any HTTP errors or parsing /// failures will return a corresponding `Error`. - pub fn new(openid_conf_endpoints: Vec<&str>) -> Result { + pub fn new(openid_conf_endpoints: Vec<&str>) -> Result { let mut idp_configs = HashMap::new(); - let http_client = Client::builder().build().map_err(Error::Http)?; + let http_client = Client::builder() + .build() + .map_err(|e| InitError::KeyService(Error::Http(e)))?; // fetch IDP configs for endpoint in openid_conf_endpoints { let conf_src: OpenIdConfigSource = http_client .get(endpoint) .send() - .map_err(Error::Http)? + .map_err(|e| InitError::KeyService(Error::Http(e)))? .error_for_status() - .map_err(Error::Http)? + .map_err(|e| InitError::KeyService(Error::Http(e)))? .json() - .map_err(Error::RequestDeserialization)?; + .map_err(|e| InitError::KeyService(Error::RequestDeserialization(e)))?; let (issuer, conf) = OpenIdConfig::from_source(conf_src); idp_configs.insert(issuer, conf); } @@ -55,15 +100,22 @@ impl KeyService { let jwks: JwkSet = http_client .get(&*conf.jwks_uri) .send() - .map_err(Error::Http)? + .map_err(|e| InitError::KeyService(Error::Http(e)))? .error_for_status() - .map_err(Error::Http)? + .map_err(|e| InitError::KeyService(Error::Http(e)))? .json() - .map_err(Error::RequestDeserialization)?; - let mut decoding_keys = conf.decoding_keys.write().map_err(|_| Error::Lock)?; + .map_err(|e| InitError::KeyService(Error::RequestDeserialization(e)))?; + let mut decoding_keys = conf + .decoding_keys + .write() + .map_err(|_| InitError::KeyService(Error::Lock))?; for jwk in jwks.keys { - let decoding_key = DecodingKey::from_jwk(&jwk).map_err(Error::KeyParsing)?; - let key_id = jwk.common.key_id.ok_or(Error::MissingKeyId)?; + let decoding_key = DecodingKey::from_jwk(&jwk) + .map_err(|e| InitError::KeyService(Error::KeyParsing(e)))?; + let key_id = jwk + .common + .key_id + .ok_or(InitError::KeyService(Error::MissingKeyId))?; decoding_keys.insert(key_id.into(), Arc::new(decoding_key)); } } @@ -146,3 +198,58 @@ impl KeyService { Ok(()) } } + +/// Error type for issues encountered in the Key Service. +/// +/// The `KeyService` is responsible for retrieving and managing keys, used to +/// decode JWTs. This enum represents the various errors that can occur when +/// interacting with the key service, including network issues, parsing problems, +/// and other key-related errors. +#[derive(thiserror::Error, Debug)] + +pub enum Error { + /// The specified key ID (`kid`) was not found in the JSON Web Key Set (JWKS). + /// + /// This error occurs when the JWKS does not contain a key matching the provided + /// `kid`, which is required for verifying JWTs. + #[error("No key with `kid`=\"{0}\" found in the JWKS.")] + KeyNotFound(Box), + + /// An HTTP error occurred during the request to fetch the JWKS. + /// + /// This error occurs when the `KeyService` makes an HTTP request to retrieve + /// the JWKS and encounters issues such as connectivity failures, timeouts, or + /// invalid responses. + #[error("HTTP error occurred: {0}")] + Http(#[source] reqwest::Error), + + /// Failed to deserialize the HTTP response when fetching the JWKS. + /// + /// This error occurs when the response body from the HTTP request cannot be + /// deserialized into the expected JSON format, possibly due to invalid or + /// malformed data from the server. + #[error("Failed to deserialize the response from the HTTP request: {0}")] + RequestDeserialization(#[source] reqwest::Error), + + /// Failed to parse a decoding key from the JWKS JSON data. + /// + /// This error occurs when the JWKS contains invalid or unsupported key data + /// making it impossible to parse a valid key that can be used for JWT decoding. + #[error("Error parsing decoding key from JWKS JSON: {0}")] + KeyParsing(#[source] jsonwebtoken::errors::Error), + + /// The JSON Web Key (JWK) is missing the required `kid` field. + /// + /// The `kid` (Key ID) is necessary to identify the correct key in the JWKS + /// when verifying a JWT. This error occurs if the JWK does not contain a `kid`. + #[error("The JWK is missing a required `kid`.")] + MissingKeyId, + + /// Failed to acquire a write lock on the decoding keys. + /// + /// This error is occurs when the service attempts to acquire a write lock + /// on the set of decoding keys, but the lock is poisoned (e.g., due to a panic + /// in another thread), making it unsafe to proceed. + #[error("Failed to acquire write lock on decoding keys.")] + Lock, +} diff --git a/jans-cedarling/cedarling/src/jwt/decoding_strategy/key_service/error.rs b/jans-cedarling/cedarling/src/jwt/decoding_strategy/key_service/error.rs deleted file mode 100644 index 65ca7651309..00000000000 --- a/jans-cedarling/cedarling/src/jwt/decoding_strategy/key_service/error.rs +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This software is available under the Apache-2.0 license. - * See https://www.apache.org/licenses/LICENSE-2.0.txt for full text. - * - * Copyright (c) 2024, Gluu, Inc. - */ - -/// Error type for the Key Service -#[derive(thiserror::Error, Debug)] -pub enum Error { - /// Indicates that a key with the specified `kid` was not found in the JWKS. - #[error("No key with `kid`=\"{0}\" found in the JWKS.")] - KeyNotFound(Box), - - /// Represents an HTTP error during the request. - #[error("HTTP error occurred: {0}")] - Http(#[source] reqwest::Error), - - /// Indicates failure to deserialize the response from the HTTP request. - #[error("Failed to deserialize the response from the HTTP request: {0}")] - RequestDeserialization(#[source] reqwest::Error), - - /// Indicates an error in parsing the decoding key from the JWKS JSON. - #[error("Error parsing decoding key from JWKS JSON: {0}")] - KeyParsing(#[source] jsonwebtoken::errors::Error), - - /// Indicates that the JWK is missing a `kid`. - #[error("The JWK is missing a required `kid`.")] - MissingKeyId, - - /// Indicates that acquiring a write lock on decoding keys failed. - /// - /// This error gets returned when a lock gets poisoned. - #[error("Failed to acquire write lock on decoding keys.")] - Lock, -} diff --git a/jans-cedarling/cedarling/src/jwt/decoding_strategy/key_service/openid_config.rs b/jans-cedarling/cedarling/src/jwt/decoding_strategy/key_service/openid_config.rs deleted file mode 100644 index 315763fc700..00000000000 --- a/jans-cedarling/cedarling/src/jwt/decoding_strategy/key_service/openid_config.rs +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This software is available under the Apache-2.0 license. - * See https://www.apache.org/licenses/LICENSE-2.0.txt for full text. - * - * Copyright (c) 2024, Gluu, Inc. - */ - -use jsonwebtoken::DecodingKey; -use serde::Deserialize; -use std::{ - collections::HashMap, - sync::{Arc, RwLock}, -}; - -/// represents the source data for OpenID configuration. -#[derive(Deserialize)] -pub struct OpenIdConfigSource { - issuer: Box, - jwks_uri: Box, - // The following values are also normally returned when sending - // a GET request to the `openid_configuration_endpoint` but are - // not currently being used. - // - // authorization_endpoint: Box, - // device_authorization_endpoint: Box, - // token_endpoint: Box, - // userinfo_endpoint: Box, - // revocation_endpoint: Box, - // response_types_supported: Vec>, - // subject_types_supported: Vec>, - // id_token_signing_algs_values_supported: Vec>, - // scopes_supported: Vec>, - // claims_supported: Vec>, -} - -/// represents the OpenID configuration for an identity provider. -pub struct OpenIdConfig { - pub jwks_uri: Box, - // - pub decoding_keys: Arc, Arc>>>, -} - -impl OpenIdConfig { - /// creates an `OpenIdConfig` from the provided source. - /// - /// this method extracts the issuer and constructs a new `OpenIdConfig` - /// instance, initializing the decoding keys storage. - pub fn from_source(src: OpenIdConfigSource) -> (Box, OpenIdConfig) { - let issuer = src.issuer; - ( - issuer, - OpenIdConfig { - jwks_uri: src.jwks_uri, - decoding_keys: Arc::new(RwLock::new(HashMap::new())), - }, - ) - } -} diff --git a/jans-cedarling/cedarling/src/jwt/decoding_strategy/traits.rs b/jans-cedarling/cedarling/src/jwt/decoding_strategy/traits.rs deleted file mode 100644 index d55123ec008..00000000000 --- a/jans-cedarling/cedarling/src/jwt/decoding_strategy/traits.rs +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This software is available under the Apache-2.0 license. - * See https://www.apache.org/licenses/LICENSE-2.0.txt for full text. - * - * Copyright (c) 2024, Gluu, Inc. - */ - -use super::Error; -use serde::de::DeserializeOwned; - -/// Trait for decoding a JWT token from a string representation. -/// -/// # Usage -/// This trait is meant to be implemented for structs that need to handle JWT -/// token decoding logic. It is particularly useful for working with various -/// token types, ensuring they can be decoded into usable types in Rust. -pub trait Decode: Send + Sync { - /// Decodes the JWT token string into the specified type. - /// - /// This method takes the token in its string format and decodes it into a - /// generic type `T`. It can also optionally verify the audience (`aud`), - /// issuer (`iss`), and the presence of the subject (`sub`) claim. - /// - /// # Errors - /// Returns an error if the decoding process fails or if validation fails - /// (e.g., the token is invalid or its signature is untrusted). - fn decode( - &self, - jwt_str: &str, - aud: Option, - iss: Option, - req_sub: bool, - ) -> Result; -} - -/// Trait for extracting claims from a JWT token without validation. -/// -/// # Usage -/// Implement this trait when you need to handle tokens where validation is not -/// required, but you still want to extract the claims in a structured manner. -pub trait ExtractClaims: Send + Sync { - /// Extracts the claims from the JWT token string without validating the - /// signature or other parameters, returning the claims as the specified type. - /// - /// # Errors - /// Returns an error if the decoding process fails or if the claims cannot - /// be extracted. - fn extract_claims(&self, jwt: &str) -> Result; -} diff --git a/jans-cedarling/cedarling/src/jwt/error.rs b/jans-cedarling/cedarling/src/jwt/error.rs deleted file mode 100644 index d204dc90c0f..00000000000 --- a/jans-cedarling/cedarling/src/jwt/error.rs +++ /dev/null @@ -1,53 +0,0 @@ -/* - * This software is available under the Apache-2.0 license. - * See https://www.apache.org/licenses/LICENSE-2.0.txt for full text. - * - * Copyright (c) 2024, Gluu, Inc. - */ - -use super::decoding_strategy::key_service; - -/// Error type for the JWT service. -#[derive(thiserror::Error, Debug)] -pub enum Error { - /// Indicates an error encountered while parsing the JWT. - /// - /// This variant occurs when the provided JWT cannot be properly parsed. - #[error("Error parsing the JWT: {0}")] - Parsing(#[source] jsonwebtoken::errors::Error), - - /// Indicates that the token was signed with an unsupported algorithm. - /// - /// This occurs when the JWT header specifies an algorithm that is not supported - /// by the current validation configuration. - #[error("The JWT is signed with an unsupported algorithm: {0:?}")] - TokenSignedWithUnsupportedAlgorithm(jsonwebtoken::Algorithm), - - /// Indicates that a required header is missing from the JWT. - /// - /// The JWT specification requires certain headers (e.g., `kid` for key identification). - /// This error is returned if any of those required headers are missing. - #[error("The JWT is missing a required header: {0}")] - MissingRequiredHeader(String), - - /// Indicates failure during the validation of the JWT. - /// - /// This occurs when JWT validation fails due to issues such as an invalid signature, - /// claim validation failure, or other validation errors. - #[error("Failed to validate the JWT: {0}")] - Validation(#[source] jsonwebtoken::errors::Error), - - /// Indicates an unsupported algorithm defined in the configuration. - /// - /// This variant indicates that the configuration specifies an algorithm that - /// is not yet implemented or recognized by the JWT service. - #[error("An algorithm defined in the configuration is not yet implemented: {0}")] - UnimplementedAlgorithm(Box), - - /// Indicates an error from the key service when retrieving or handling keys. - /// - /// This occurs when the `KeyService` fails to retrieve a required key or encounters - /// another key-related issue during JWT validation. - #[error("Key service error: {0}")] - KeyService(#[from] key_service::Error), -} diff --git a/jans-cedarling/cedarling/src/jwt/jwt_service_config.rs b/jans-cedarling/cedarling/src/jwt/jwt_service_config.rs index cb9fb33f1ae..f15d39db522 100644 --- a/jans-cedarling/cedarling/src/jwt/jwt_service_config.rs +++ b/jans-cedarling/cedarling/src/jwt/jwt_service_config.rs @@ -9,7 +9,6 @@ //! This configuration allows to initialize service without any errors. use crate::common::policy_store::TrustedIssuer; -use crate::jwt; /// Configuration for JWT service pub enum JwtServiceConfig { @@ -18,7 +17,7 @@ pub enum JwtServiceConfig { /// Decoding strategy that performs validation using a key service and supported algorithms. WithValidation { - supported_algs: Vec, + supported_algs: Vec, trusted_idps: Vec, }, } diff --git a/jans-cedarling/cedarling/src/jwt/mod.rs b/jans-cedarling/cedarling/src/jwt/mod.rs index 1f5ae302ce9..d66606aeb54 100644 --- a/jans-cedarling/cedarling/src/jwt/mod.rs +++ b/jans-cedarling/cedarling/src/jwt/mod.rs @@ -14,16 +14,12 @@ //! - Verifying the validity of JWTs based on claims such as expiration time and audience. mod decoding_strategy; -mod error; mod jwt_service_config; #[cfg(test)] mod test; mod token; -pub use decoding_strategy::string_to_alg; -use decoding_strategy::DecodingStrategy; -pub use error::*; -pub use jsonwebtoken::Algorithm; +use decoding_strategy::{key_service, DecodingStrategy}; pub use jwt_service_config::*; use serde::de::DeserializeOwned; use token::*; @@ -50,20 +46,20 @@ impl JwtService { } /// Initializes a new `JwtService` instance based on the provided configuration. - pub(crate) fn new_with_config(config: JwtServiceConfig) -> Self { + pub(crate) fn new_with_config(config: JwtServiceConfig) -> Result { match config { JwtServiceConfig::WithoutValidation => { let decoding_strategy = DecodingStrategy::new_without_validation(); - Self { decoding_strategy } + Ok(Self { decoding_strategy }) }, JwtServiceConfig::WithValidation { supported_algs, trusted_idps, } => { let decoding_strategy = - DecodingStrategy::new_with_validation(supported_algs, trusted_idps) - .expect("could not initialize decoding strategy with validation"); - Self { decoding_strategy } + DecodingStrategy::new_with_validation(supported_algs, trusted_idps)?; + + Ok(Self { decoding_strategy }) }, } } @@ -124,3 +120,52 @@ impl JwtService { Ok((access_token_claims, id_token_claims, userinfo_token_claims)) } } + +/// Error type for initialization failures in the JWT service. +/// +/// This enum represents errors that may occur during the initialization of +/// components within the JWT service, such as the decoding strategy and key service. +/// It provides a way to propagate errors from lower-level modules to the top level. +#[derive(thiserror::Error, Debug)] +pub enum InitError { + /// An error occurred while initializing the decoding strategy. + /// + /// This error occurs when the `DecodingStrategy` failed to initialize, + /// potentially due to issues such as invalid configuration, missing keys, or + /// other initialization errors. + #[error("Error initializing Decoding Strategy: {0}")] + DecodingStrategy(#[from] decoding_strategy::Error), + + /// An error occurred while initializing the key service. + /// + /// This error occurs when the `KeyService` could not be initialized, + /// which may happen due to problems like failing to retrieve necessary keys + /// or encountering network errors while accessing a key provider. + #[error("Error initializing Key Service: {0}")] + KeyService(#[from] key_service::Error), +} + +/// Error type for issues encountered in the JWT service. +/// +/// This enum encapsulates various errors that may arise while decoding or +/// validating JWTs within the service. It serves as a central point for +/// handling errors related to JWT processing, allowing easy propagation +/// of errors from lower-level components. +#[derive(thiserror::Error, Debug)] +pub enum Error { + /// An error occurred during JWT decoding. + /// + /// This error occurs when the JWT could not be decoded properly, + /// which may be due to issues such as malformed tokens or unsupported + /// algorithms in the JWT header. + #[error("Error decoding JWT: {0}")] + Decoding(#[from] decoding_strategy::Error), + + /// An error from the key service occurred while processing JWTs. + /// + /// This error occurs when there was an issue related to key retrieval, + /// management, or validation during JWT processing, originating from the + /// `KeyService`. + #[error("Key Service error: {0}")] + KeyService(#[from] key_service::Error), +}