jwt_compact_frame/
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 type CborDeError<E = anyhow::Error> = ciborium::de::Error<E>;
11#[cfg(feature = "ciborium")]
12pub 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	/// Payload is too large.
35	ClaimsTooLarge,
36	/// Signature too large.
37	SignatureTooLarge,
38	/// Signed data too large.
39	SignedDataTooLarge,
40}
41
42impl fmt::Display for ParseError {
43	fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
44		match self {
45			Self::InvalidTokenStructure => formatter.write_str("invalid token structure"),
46			Self::InvalidBase64Encoding => write!(formatter, "invalid base64 decoding"),
47			Self::MalformedHeader(err) => write!(formatter, "malformed token header: {err}"),
48			Self::UnsupportedContentType(ty) => {
49				write!(formatter, "unsupported content type: {ty}")
50			},
51			Self::ClaimsTooLarge => {
52				write!(formatter, "claims too large")
53			},
54			Self::SignatureTooLarge => {
55				write!(formatter, "signature too large")
56			},
57			Self::SignedDataTooLarge => {
58				write!(formatter, "signed data too large")
59			},
60		}
61	}
62}
63
64#[cfg(feature = "std")]
65impl std::error::Error for ParseError {
66	fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
67		match self {
68			Self::MalformedHeader(err) => Some(err),
69			_ => None,
70		}
71	}
72}
73
74/// Errors that can occur during token validation.
75#[derive(Debug)]
76#[non_exhaustive]
77pub enum ValidationError {
78	/// Algorithm mentioned in the token header differs from invoked one.
79	AlgorithmMismatch {
80		/// Expected algorithm name.
81		expected: String,
82		/// Actual algorithm in the token.
83		actual: String,
84	},
85	/// Token signature has invalid byte length.
86	InvalidSignatureLen {
87		/// Expected signature length.
88		expected: usize,
89		/// Actual signature length.
90		actual: usize,
91	},
92	/// Token signature is malformed.
93	MalformedSignature(anyhow::Error),
94	/// Token signature has failed verification.
95	InvalidSignature,
96	/// Token claims cannot be deserialized from JSON.
97	MalformedClaims(serde_json::Error),
98	/// Token claims cannot be deserialized from CBOR.
99	#[cfg(feature = "ciborium")]
100	#[cfg_attr(docsrs, doc(cfg(feature = "ciborium")))]
101	MalformedCborClaims(CborDeError),
102	/// Claim requested during validation is not present in the token.
103	NoClaim(Claim),
104	/// Token has expired.
105	Expired,
106	/// Token is not yet valid as per `nbf` claim.
107	NotMature,
108}
109
110/// Identifier of a claim in `Claims`.
111#[derive(Debug, Clone, PartialEq, Eq)]
112#[non_exhaustive]
113pub enum Claim {
114	/// `exp` claim (expiration time).
115	Expiration,
116	/// `nbf` claim (valid not before).
117	NotBefore,
118}
119
120impl fmt::Display for Claim {
121	fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
122		formatter.write_str(match self {
123			Self::Expiration => "exp",
124			Self::NotBefore => "nbf",
125		})
126	}
127}
128
129impl fmt::Display for ValidationError {
130	fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
131		match self {
132			Self::AlgorithmMismatch { expected, actual } => {
133				write!(formatter, "token algorithm ({actual}) differs from expected ({expected})")
134			},
135			Self::InvalidSignatureLen { expected, actual } => {
136				write!(formatter, "invalid signature length: expected {expected} bytes, got {actual} bytes")
137			},
138			Self::MalformedSignature(err) => write!(formatter, "malformed token signature: {err}"),
139			Self::InvalidSignature => formatter.write_str("signature has failed verification"),
140			Self::MalformedClaims(err) => write!(formatter, "cannot deserialize claims: {err}"),
141			#[cfg(feature = "ciborium")]
142			Self::MalformedCborClaims(err) => write!(formatter, "cannot deserialize claims: {err}"),
143			Self::NoClaim(claim) => {
144				write!(formatter, "claim `{claim}` requested during validation is not present in the token")
145			},
146			Self::Expired => formatter.write_str("token has expired"),
147			Self::NotMature => formatter.write_str("token is not yet ready"),
148		}
149	}
150}
151
152#[cfg(feature = "std")]
153impl std::error::Error for ValidationError {
154	fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
155		match self {
156			Self::MalformedSignature(err) => Some(err.as_ref()),
157			Self::MalformedClaims(err) => Some(err),
158			#[cfg(feature = "ciborium")]
159			Self::MalformedCborClaims(err) => Some(err),
160			_ => None,
161		}
162	}
163}
164
165/// Errors that can occur during token creation.
166#[derive(Debug)]
167#[non_exhaustive]
168pub enum CreationError {
169	/// Token header cannot be serialized.
170	Header(serde_json::Error),
171	/// Token claims cannot be serialized into JSON.
172	Claims(serde_json::Error),
173	/// Token claims cannot be serialized into CBOR.
174	#[cfg(feature = "ciborium")]
175	#[cfg_attr(docsrs, doc(cfg(feature = "ciborium")))]
176	CborClaims(CborSerError),
177}
178
179impl fmt::Display for CreationError {
180	fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
181		match self {
182			Self::Header(err) => write!(formatter, "cannot serialize header: {err}"),
183			Self::Claims(err) => write!(formatter, "cannot serialize claims: {err}"),
184			#[cfg(feature = "ciborium")]
185			Self::CborClaims(err) => write!(formatter, "cannot serialize claims into CBOR: {err}"),
186		}
187	}
188}
189
190#[cfg(feature = "std")]
191impl std::error::Error for CreationError {
192	fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
193		match self {
194			Self::Header(err) | Self::Claims(err) => Some(err),
195			#[cfg(feature = "ciborium")]
196			Self::CborClaims(err) => Some(err),
197		}
198	}
199}