pdk-jwt-lib 1.10.0

PDK JWT Library
Documentation
// Copyright (c) 2026, Salesforce, Inc.,
// All rights reserved.
// For full license text, see the LICENSE.txt file

//! # JWT Error Types
//!
//! This module contains error types used across the JWT library for handling
//! various failure scenarios during JWT validation and processing.
//!
//! ## Primary types
//!
//! - [`JWTError`]: error type for JWT operations
//! - [`JWTErrorEnumKind`]: error type for JWT operations (for testing purposes)

use std::error::Error;
use std::fmt;

use base64::DecodeError;
use pem::PemError;
use serde_json::Error as SerdeError;

#[cfg(not(any(feature = "fips", fips)))]
use jwt_compact::{ParseError, ValidationError};

/// Possible errors when handling the JWT validation lifecycle.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum JWTError {
    /// Authorization header is missing from the request
    AuthorizationHeaderNotPresent,
    /// Bearer token format is malformed
    BearerMalformed,
    /// Bearer token is not present in the Authorization header
    BearerNotPresent,
    /// Audience claim validation failed
    ClaimAudValidationFailed,
    /// Required audience claim is missing
    ClaimAudNotPresent,
    /// Expiration claim validation failed
    ClaimExpValidationFailed,
    /// Required expiration claim is missing
    ClaimExpNotPresent,
    /// Not before claim validation failed
    ClaimNbValidationFailed,
    /// Required not before claim is missing
    ClaimNbNotPresent,
    /// Failed to parse ES (ECDSA) public key
    EsPublicKeyParseFailed(String),
    /// Expression processing failed during claim evaluation
    ExpressionProcessingFailed(String),
    /// Invalid key length for the specified algorithm
    InvalidKeyLength(String),
    /// Header handler is missing for key provision
    KeyProvisionMissingHeaderHandler,
    /// Key ID (kid) claim is missing
    KidNotPresent,
    /// Mandatory custom claim is missing or doesn't match expected value
    MandatoryCustomClaimMismatchOrNotPresent,
    /// Non-mandatory custom claim doesn't match expected value
    NonMandatoryCustomClaimMismatch,
    /// Failed to parse RSA public key
    RsaPublicKeyParseFailed(String),
    /// Failed to parse signing algorithm
    SigningAlgorithmParseFailed,
    /// Failed to parse signing key length
    SigningKeyLengthParseFailed,
    /// Failed to parse JWT token structure
    TokenParseFailed(String),
    /// General validation failed with specific reason
    ValidationFailed(String),
}

#[cfg(test)]
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum JWTErrorEnumKind {
    AuthorizationHeaderNotPresent,
    BearerMalformed,
    BearerNotPresent,
    ClaimAudValidationFailed,
    ClaimAudNotPresent,
    ClaimExpValidationFailed,
    ClaimExpNotPresent,
    ClaimNbValidationFailed,
    ClaimNbNotPresent,
    EsPublicKeyParseFailed,
    ExpressionProcessingFailed,
    InvalidKeyLength,
    KeyProvisionMissingHeaderHandler,
    KidNotPresent,
    MandatoryCustomClaimMismatchOrNotPresent,
    NonMandatoryCustomClaimMismatch,
    RsaPublicKeyParseFailed,
    SigningAlgorithmParseFailed,
    SigningKeyLengthParseFailed,
    TokenParseFailed,
    ValidationFailed,
}

#[cfg(test)]
impl From<&JWTError> for JWTErrorEnumKind {
    fn from(value: &JWTError) -> Self {
        match value {
            JWTError::AuthorizationHeaderNotPresent => {
                JWTErrorEnumKind::AuthorizationHeaderNotPresent
            }
            JWTError::BearerMalformed => JWTErrorEnumKind::BearerMalformed,
            JWTError::BearerNotPresent => JWTErrorEnumKind::BearerNotPresent,
            JWTError::ClaimAudValidationFailed => JWTErrorEnumKind::ClaimAudValidationFailed,
            JWTError::ClaimAudNotPresent => JWTErrorEnumKind::ClaimAudNotPresent,
            JWTError::ClaimExpValidationFailed => JWTErrorEnumKind::ClaimExpValidationFailed,
            JWTError::ClaimExpNotPresent => JWTErrorEnumKind::ClaimExpNotPresent,
            JWTError::ClaimNbValidationFailed => JWTErrorEnumKind::ClaimNbValidationFailed,
            JWTError::ClaimNbNotPresent => JWTErrorEnumKind::ClaimNbNotPresent,
            JWTError::EsPublicKeyParseFailed(_) => JWTErrorEnumKind::EsPublicKeyParseFailed,
            JWTError::ExpressionProcessingFailed(_) => JWTErrorEnumKind::ExpressionProcessingFailed,
            JWTError::InvalidKeyLength(_) => JWTErrorEnumKind::InvalidKeyLength,
            JWTError::KeyProvisionMissingHeaderHandler => {
                JWTErrorEnumKind::KeyProvisionMissingHeaderHandler
            }
            JWTError::KidNotPresent => JWTErrorEnumKind::KidNotPresent,
            JWTError::MandatoryCustomClaimMismatchOrNotPresent => {
                JWTErrorEnumKind::MandatoryCustomClaimMismatchOrNotPresent
            }
            JWTError::NonMandatoryCustomClaimMismatch => {
                JWTErrorEnumKind::NonMandatoryCustomClaimMismatch
            }
            JWTError::RsaPublicKeyParseFailed(_) => JWTErrorEnumKind::RsaPublicKeyParseFailed,
            JWTError::SigningAlgorithmParseFailed => JWTErrorEnumKind::SigningAlgorithmParseFailed,
            JWTError::SigningKeyLengthParseFailed => JWTErrorEnumKind::SigningKeyLengthParseFailed,
            JWTError::TokenParseFailed(_) => JWTErrorEnumKind::TokenParseFailed,
            JWTError::ValidationFailed(_) => JWTErrorEnumKind::ValidationFailed,
        }
    }
}

impl fmt::Display for JWTError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.get_message())
    }
}

impl Error for JWTError {}

impl JWTError {
    /// Get error message.
    pub fn get_message(&self) -> &str {
        match self {
            JWTError::AuthorizationHeaderNotPresent => "JWT Token is required.",
            JWTError::BearerMalformed => "Bearer malformed.",
            JWTError::BearerNotPresent => "Invalid token. Not a bearer authorization header.",
            JWTError::ClaimAudValidationFailed => "Invalid token. Aud claim validation error.",
            JWTError::ClaimAudNotPresent => "Invalid token. Aud claim not present.",
            JWTError::ClaimExpValidationFailed => "Invalid token. Exp claim validation error.",
            JWTError::ClaimExpNotPresent => "Invalid token. Exp claim not present.",
            JWTError::ClaimNbValidationFailed => "Invalid token. Nb claim validation error.",
            JWTError::ClaimNbNotPresent => "Invalid token. Nb claim not present.",
            JWTError::EsPublicKeyParseFailed(message) => message,
            JWTError::ExpressionProcessingFailed(message) => message,
            JWTError::InvalidKeyLength(message) => message,
            JWTError::KeyProvisionMissingHeaderHandler => {
                "Expected header handler for key provision."
            }
            JWTError::KidNotPresent => "Invalid token. Missing 'kid' header.",
            JWTError::MandatoryCustomClaimMismatchOrNotPresent => {
                "Invalid token. Mandatory custom claim mismatch or not present."
            }
            JWTError::NonMandatoryCustomClaimMismatch => {
                "Invalid token. Non mandatory custom claim mismatch."
            }
            JWTError::RsaPublicKeyParseFailed(message) => message,
            JWTError::TokenParseFailed(message) => message,
            JWTError::SigningAlgorithmParseFailed => "Could not parse SigningAlgorithm from str",
            JWTError::SigningKeyLengthParseFailed => "Could not parse SigningKeyLength from str",
            JWTError::ValidationFailed(message) => message,
        }
    }
}

// Error mappings

#[cfg(not(any(feature = "fips", fips)))]
impl From<ValidationError> for JWTError {
    fn from(e: ValidationError) -> Self {
        match e {
            ValidationError::MalformedClaims(err) => {
                JWTError::TokenParseFailed(format!("Token parsing error: {err}"))
            }
            _ => JWTError::ValidationFailed(format!(
                "Invalid token. JWT Policy validation error: {e}"
            )),
        }
    }
}

#[cfg(not(any(feature = "fips", fips)))]
impl From<jwt_simple::Error> for JWTError {
    fn from(e: jwt_simple::Error) -> Self {
        JWTError::ValidationFailed(format!("JWT Policy validation error: {e}"))
    }
}

impl From<PemError> for JWTError {
    fn from(e: PemError) -> Self {
        JWTError::RsaPublicKeyParseFailed(format!("Public key parsing error: {e}"))
    }
}

#[cfg(not(any(feature = "fips", fips)))]
impl From<ParseError> for JWTError {
    fn from(e: ParseError) -> Self {
        JWTError::TokenParseFailed(format!("Token parsing error: {e}"))
    }
}

impl From<DecodeError> for JWTError {
    fn from(e: DecodeError) -> Self {
        JWTError::TokenParseFailed(format!("Token decoding error: {e}"))
    }
}

impl From<SerdeError> for JWTError {
    fn from(e: SerdeError) -> Self {
        JWTError::TokenParseFailed(format!("Token parsing error: {e}"))
    }
}