modo/auth/session/jwt/
error.rs1use std::fmt;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
23pub enum JwtError {
24 MissingToken,
27 InvalidHeader,
29 MalformedToken,
31 DeserializationFailed,
33 InvalidSignature,
35 Expired,
37 NotYetValid,
39 InvalidIssuer,
41 InvalidAudience,
43 AlgorithmMismatch,
45 SigningFailed,
48 SerializationFailed,
50}
51
52impl JwtError {
53 pub fn code(&self) -> &'static str {
58 match self {
59 Self::MissingToken => "jwt:missing_token",
60 Self::InvalidHeader => "jwt:invalid_header",
61 Self::MalformedToken => "jwt:malformed_token",
62 Self::DeserializationFailed => "jwt:deserialization_failed",
63 Self::InvalidSignature => "jwt:invalid_signature",
64 Self::Expired => "jwt:expired",
65 Self::NotYetValid => "jwt:not_yet_valid",
66 Self::InvalidIssuer => "jwt:invalid_issuer",
67 Self::InvalidAudience => "jwt:invalid_audience",
68 Self::AlgorithmMismatch => "jwt:algorithm_mismatch",
69 Self::SigningFailed => "jwt:signing_failed",
70 Self::SerializationFailed => "jwt:serialization_failed",
71 }
72 }
73}
74
75impl fmt::Display for JwtError {
76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77 match self {
78 Self::MissingToken => write!(f, "missing token"),
79 Self::InvalidHeader => write!(f, "invalid token header"),
80 Self::MalformedToken => write!(f, "malformed token"),
81 Self::DeserializationFailed => write!(f, "failed to deserialize token claims"),
82 Self::InvalidSignature => write!(f, "invalid token signature"),
83 Self::Expired => write!(f, "token has expired"),
84 Self::NotYetValid => write!(f, "token is not yet valid"),
85 Self::InvalidIssuer => write!(f, "invalid token issuer"),
86 Self::InvalidAudience => write!(f, "invalid token audience"),
87 Self::AlgorithmMismatch => write!(f, "token algorithm mismatch"),
88 Self::SigningFailed => write!(f, "failed to sign token"),
89 Self::SerializationFailed => write!(f, "failed to serialize token claims"),
90 }
91 }
92}
93
94impl std::error::Error for JwtError {}
95
96#[cfg(test)]
97mod tests {
98 use super::*;
99 use crate::Error;
100
101 #[test]
102 fn all_variants_have_unique_codes() {
103 let variants = [
104 JwtError::MissingToken,
105 JwtError::InvalidHeader,
106 JwtError::MalformedToken,
107 JwtError::DeserializationFailed,
108 JwtError::InvalidSignature,
109 JwtError::Expired,
110 JwtError::NotYetValid,
111 JwtError::InvalidIssuer,
112 JwtError::InvalidAudience,
113 JwtError::AlgorithmMismatch,
114 JwtError::SigningFailed,
115 JwtError::SerializationFailed,
116 ];
117 let mut codes: Vec<&str> = variants.iter().map(|v| v.code()).collect();
118 let len_before = codes.len();
119 codes.sort();
120 codes.dedup();
121 assert_eq!(codes.len(), len_before, "duplicate error codes found");
122 }
123
124 #[test]
125 fn all_codes_start_with_jwt_prefix() {
126 let variants = [
127 JwtError::MissingToken,
128 JwtError::Expired,
129 JwtError::SigningFailed,
130 ];
131 for v in &variants {
132 assert!(
133 v.code().starts_with("jwt:"),
134 "code {} missing prefix",
135 v.code()
136 );
137 }
138 }
139
140 #[test]
141 fn display_is_human_readable() {
142 assert_eq!(JwtError::Expired.to_string(), "token has expired");
143 assert_eq!(JwtError::MissingToken.to_string(), "missing token");
144 }
145
146 #[test]
147 fn recoverable_via_source_as() {
148 let err = Error::unauthorized("unauthorized")
149 .chain(JwtError::Expired)
150 .with_code(JwtError::Expired.code());
151 let jwt_err = err.source_as::<JwtError>();
152 assert_eq!(jwt_err, Some(&JwtError::Expired));
153 assert_eq!(err.error_code(), Some("jwt:expired"));
154 }
155}