#![allow(deprecated)]
use thiserror::Error;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum Error {
#[error("token has expired")]
Expired,
#[error("token cannot be used before {0}")]
NotYetValid(String),
#[error("missing required claim: {0}")]
MissingClaim(String),
#[error("claim '{claim}' invalid: expected {expected}, got {actual}")]
InvalidClaimValue {
claim: String,
expected: String,
actual: String,
},
#[error("'{0}' is a reserved claim key")]
ReservedClaimKey(String),
#[error("duplicate claim: '{0}'")]
DuplicateClaim(String),
#[error("could not convert claim '{0}' to expected type")]
UnexpectedClaimType(String),
#[error("custom validation failed for claim '{0}'")]
CustomValidationFailed(String),
#[error("malformed RFC3339 date: {0}")]
MalformedDate(String),
#[error("invalid email address: {0}")]
InvalidEmail(String),
#[error("cryptographic operation failed")]
CryptoError,
#[error("invalid key format")]
InvalidKey,
#[error("signature verification failed")]
InvalidSignature,
#[error("invalid token structure")]
InvalidTokenStructure,
#[error("invalid token header")]
InvalidHeader,
#[error("footer mismatch")]
FooterMismatch,
#[error("token exceeds maximum permitted size")]
TokenTooLarge,
#[error("footer exceeds maximum permitted size")]
FooterTooLarge,
#[error("JSON error: {0}")]
Json(#[from] serde_json::Error),
#[error("base64 decoding failed: {0}")]
Base64Decode(#[from] base64::DecodeError),
#[error("UTF-8 error: {0}")]
Utf8(#[from] std::str::Utf8Error),
#[error("UTF-8 error: {0}")]
FromUtf8(#[from] std::string::FromUtf8Error),
#[error("time format error: {0}")]
TimeFormat(#[from] time::error::Format),
}
pub type Result<T> = std::result::Result<T, Error>;
#[cfg(feature = "generic")]
impl From<crate::generic::PasetoClaimError> for Error {
fn from(err: crate::generic::PasetoClaimError) -> Self {
use crate::generic::PasetoClaimError;
match err {
PasetoClaimError::Expired => Error::Expired,
PasetoClaimError::UseBeforeAvailable(time) => Error::NotYetValid(time),
PasetoClaimError::RFC3339Date(date) => Error::MalformedDate(date),
PasetoClaimError::Missing(claim) => Error::MissingClaim(claim),
PasetoClaimError::Unexpected(claim) => Error::UnexpectedClaimType(claim),
PasetoClaimError::CustomValidation(claim) => Error::CustomValidationFailed(claim),
PasetoClaimError::Invalid(claim, expected, actual) => Error::InvalidClaimValue {
claim,
expected,
actual,
},
PasetoClaimError::Reserved(key) => Error::ReservedClaimKey(key),
PasetoClaimError::DuplicateTopLevelPayloadClaim(claim) => Error::DuplicateClaim(claim),
}
}
}
#[cfg(feature = "generic")]
impl From<crate::generic::GenericBuilderError> for Error {
fn from(err: crate::generic::GenericBuilderError) -> Self {
use crate::generic::GenericBuilderError;
match err {
GenericBuilderError::ClaimError { source } => source.into(),
GenericBuilderError::BadEmailAddress(email) => Error::InvalidEmail(email),
GenericBuilderError::DuplicateTopLevelPayloadClaim(claim) => {
Error::DuplicateClaim(claim)
}
GenericBuilderError::CipherError { source } => source.into(),
GenericBuilderError::PayloadJsonError { source } => Error::Json(source),
}
}
}
#[cfg(feature = "generic")]
impl From<crate::generic::GenericParserError> for Error {
fn from(err: crate::generic::GenericParserError) -> Self {
use crate::generic::GenericParserError;
match err {
GenericParserError::ClaimError { source } => source.into(),
GenericParserError::CipherError { source } => source.into(),
GenericParserError::PayloadJsonError { source } => Error::Json(source),
}
}
}
#[cfg(feature = "core")]
impl From<crate::core::PasetoError> for Error {
fn from(err: crate::core::PasetoError) -> Self {
use crate::core::PasetoError;
match err {
PasetoError::PasetoCipherError(_) => Error::CryptoError,
PasetoError::Cryption => Error::CryptoError,
PasetoError::InvalidKey => Error::InvalidKey,
PasetoError::Signature => Error::CryptoError,
PasetoError::KeyRejected { .. } => Error::InvalidKey,
PasetoError::Cipher { .. } => Error::CryptoError,
#[cfg(feature = "ed25519-dalek")]
PasetoError::RsaCipher { .. } => Error::CryptoError,
#[cfg(feature = "p384")]
PasetoError::ECSDAError { .. } => Error::CryptoError,
#[cfg(feature = "blake2")]
PasetoError::InvalidLength { .. } => Error::CryptoError,
PasetoError::InvalidSignature => Error::InvalidSignature,
PasetoError::TryFromSlice { .. } => Error::CryptoError,
PasetoError::IncorrectSize => Error::InvalidTokenStructure,
PasetoError::WrongHeader => Error::InvalidHeader,
PasetoError::FooterInvalid => Error::FooterMismatch,
PasetoError::PayloadBase64Decode { source } => Error::Base64Decode(source),
PasetoError::Utf8Error { source } => Error::Utf8(source),
PasetoError::ChaChaCipherError => Error::CryptoError,
PasetoError::Infallible { .. } => {
unreachable!("Infallible error should never be constructed")
}
PasetoError::FromUtf8Error { source } => Error::FromUtf8(source),
PasetoError::TokenTooLarge => Error::TokenTooLarge,
PasetoError::FooterTooLarge => Error::FooterTooLarge,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
assert_eq!(format!("{}", Error::Expired), "token has expired");
assert_eq!(
format!("{}", Error::NotYetValid("2024-01-01T00:00:00Z".to_string())),
"token cannot be used before 2024-01-01T00:00:00Z"
);
assert_eq!(
format!("{}", Error::MissingClaim("sub".to_string())),
"missing required claim: sub"
);
assert_eq!(
format!(
"{}",
Error::InvalidClaimValue {
claim: "aud".to_string(),
expected: "api".to_string(),
actual: "web".to_string(),
}
),
"claim 'aud' invalid: expected api, got web"
);
}
#[test]
fn test_error_is_non_exhaustive() {
let err = Error::Expired;
match err {
Error::Expired => {}
_ => {}
}
}
}