jwt_compact/
error.rs

1//! Error handling.
2
3#[cfg(feature = "ciborium")]
4use core::convert::Infallible;
5use core::fmt;
6
7use crate::alloc::String;
8
9#[cfg(feature = "ciborium")]
10pub(crate) type CborDeError<E = anyhow::Error> = ciborium::de::Error<E>;
11#[cfg(feature = "ciborium")]
12pub(crate) type CborSerError<E = Infallible> = ciborium::ser::Error<E>;
13
14/// Errors that may occur during token parsing.
15#[derive(Debug)]
16#[non_exhaustive]
17pub enum ParseError {
18    /// Token has invalid structure.
19    ///
20    /// Valid tokens must consist of 3 base64url-encoded parts (header, claims, and signature)
21    /// separated by periods.
22    InvalidTokenStructure,
23    /// Cannot decode base64.
24    InvalidBase64Encoding,
25    /// Token header cannot be parsed.
26    MalformedHeader(serde_json::Error),
27    /// [Content type][cty] mentioned in the token header is not supported.
28    ///
29    /// Supported content types are JSON (used by default) and CBOR (only if the `ciborium`
30    /// crate feature is enabled, which it is by default).
31    ///
32    /// [cty]: https://tools.ietf.org/html/rfc7515#section-4.1.10
33    UnsupportedContentType(String),
34}
35
36impl fmt::Display for ParseError {
37    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
38        match self {
39            Self::InvalidTokenStructure => formatter.write_str("invalid token structure"),
40            Self::InvalidBase64Encoding => write!(formatter, "invalid base64 decoding"),
41            Self::MalformedHeader(err) => write!(formatter, "malformed token header: {err}"),
42            Self::UnsupportedContentType(ty) => {
43                write!(formatter, "unsupported content type: {ty}")
44            }
45        }
46    }
47}
48
49#[cfg(feature = "std")]
50impl std::error::Error for ParseError {
51    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
52        match self {
53            Self::MalformedHeader(err) => Some(err),
54            _ => None,
55        }
56    }
57}
58
59/// Errors that can occur during token validation.
60#[derive(Debug)]
61#[non_exhaustive]
62pub enum ValidationError {
63    /// Algorithm mentioned in the token header differs from invoked one.
64    AlgorithmMismatch {
65        /// Expected algorithm name.
66        expected: String,
67        /// Actual algorithm in the token.
68        actual: String,
69    },
70    /// Token signature has invalid byte length.
71    InvalidSignatureLen {
72        /// Expected signature length.
73        expected: usize,
74        /// Actual signature length.
75        actual: usize,
76    },
77    /// Token signature is malformed.
78    MalformedSignature(anyhow::Error),
79    /// Token signature has failed verification.
80    InvalidSignature,
81    /// Token claims cannot be deserialized from JSON.
82    MalformedClaims(serde_json::Error),
83    /// Token claims cannot be deserialized from CBOR.
84    #[cfg(feature = "ciborium")]
85    #[cfg_attr(docsrs, doc(cfg(feature = "ciborium")))]
86    MalformedCborClaims(CborDeError),
87    /// Claim requested during validation is not present in the token.
88    NoClaim(Claim),
89    /// Token has expired.
90    Expired,
91    /// Token is not yet valid as per `nbf` claim.
92    NotMature,
93}
94
95/// Identifier of a claim in `Claims`.
96#[derive(Debug, Clone, PartialEq, Eq)]
97#[non_exhaustive]
98pub enum Claim {
99    /// `exp` claim (expiration time).
100    Expiration,
101    /// `nbf` claim (valid not before).
102    NotBefore,
103}
104
105impl fmt::Display for Claim {
106    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
107        formatter.write_str(match self {
108            Self::Expiration => "exp",
109            Self::NotBefore => "nbf",
110        })
111    }
112}
113
114impl fmt::Display for ValidationError {
115    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
116        match self {
117            Self::AlgorithmMismatch { expected, actual } => write!(
118                formatter,
119                "token algorithm ({actual}) differs from expected ({expected})"
120            ),
121            Self::InvalidSignatureLen { expected, actual } => write!(
122                formatter,
123                "invalid signature length: expected {expected} bytes, got {actual} bytes"
124            ),
125            Self::MalformedSignature(err) => write!(formatter, "malformed token signature: {err}"),
126            Self::InvalidSignature => formatter.write_str("signature has failed verification"),
127            Self::MalformedClaims(err) => write!(formatter, "cannot deserialize claims: {err}"),
128            #[cfg(feature = "ciborium")]
129            Self::MalformedCborClaims(err) => write!(formatter, "cannot deserialize claims: {err}"),
130            Self::NoClaim(claim) => write!(
131                formatter,
132                "claim `{claim}` requested during validation is not present in the token"
133            ),
134            Self::Expired => formatter.write_str("token has expired"),
135            Self::NotMature => formatter.write_str("token is not yet ready"),
136        }
137    }
138}
139
140#[cfg(feature = "std")]
141impl std::error::Error for ValidationError {
142    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
143        match self {
144            Self::MalformedSignature(err) => Some(err.as_ref()),
145            Self::MalformedClaims(err) => Some(err),
146            #[cfg(feature = "ciborium")]
147            Self::MalformedCborClaims(err) => Some(err),
148            _ => None,
149        }
150    }
151}
152
153/// Errors that can occur during token creation.
154#[derive(Debug)]
155#[non_exhaustive]
156pub enum CreationError {
157    /// Token header cannot be serialized.
158    Header(serde_json::Error),
159    /// Token claims cannot be serialized into JSON.
160    Claims(serde_json::Error),
161    /// Token claims cannot be serialized into CBOR.
162    #[cfg(feature = "ciborium")]
163    #[cfg_attr(docsrs, doc(cfg(feature = "ciborium")))]
164    CborClaims(CborSerError),
165}
166
167impl fmt::Display for CreationError {
168    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
169        match self {
170            Self::Header(err) => write!(formatter, "cannot serialize header: {err}"),
171            Self::Claims(err) => write!(formatter, "cannot serialize claims: {err}"),
172            #[cfg(feature = "ciborium")]
173            Self::CborClaims(err) => write!(formatter, "cannot serialize claims into CBOR: {err}"),
174        }
175    }
176}
177
178#[cfg(feature = "std")]
179impl std::error::Error for CreationError {
180    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
181        match self {
182            Self::Header(err) | Self::Claims(err) => Some(err),
183            #[cfg(feature = "ciborium")]
184            Self::CborClaims(err) => Some(err),
185        }
186    }
187}