1use std::{string::FromUtf8Error, sync::Arc};
2
3use axum::{
4 http::StatusCode,
5 response::{IntoResponse, Response},
6};
7use base64::DecodeError;
8use jsonwebtoken::Algorithm;
9use serde::Serialize;
10use thiserror::Error;
11
12#[derive(Debug, Serialize, Clone, Error)]
13#[serde(rename_all = "camelCase", tag = "code")]
14pub enum AuthError {
15 #[error("missing authorization header")]
16 MissingAuthHeader,
17
18 #[error("key of algorithm `{0:?}` is undefined")]
19 InvalidAlgorithm(Algorithm),
20
21 #[error("invalid authorization format: expected 'Bearer <token>'")]
22 InvalidAuthFormat,
23
24 #[error("no key id fed")]
25 InvalidKeyId,
26
27 #[error("Some of the text was invalid UTF-8: {0}")]
28 InvalidUtf8(
29 #[from]
30 #[serde(skip)]
31 FromUtf8Error,
32 ),
33
34 #[error("An error happened while serializing/deserializing JSON: {0}")]
35 InvalidJson(
36 #[from]
37 #[serde(skip)]
38 Arc<serde_json::Error>,
39 ),
40
41 #[error("An error happened when decoding some base64 text: {0}")]
42 InvalidBase64(
43 #[from]
44 #[serde(skip)]
45 DecodeError,
46 ),
47
48 #[error("token is invalid")]
49 InvalidToken,
50
51 #[error("token has expired")]
52 TokenExpired,
53
54 #[error("token is not yet valid")]
55 TokenNotYetValid,
56
57 #[error("invalid signature")]
58 InvalidSignature,
59
60 #[error("untrusted issuer")]
61 InvalidIssuer,
62
63 #[error("invalid audience")]
64 InvalidAudience,
65
66 #[error("invalid subject")]
67 InvalidSubject,
68
69 #[error("required claim `{0}` missing")]
70 MissingClaim(String),
71
72 #[error("insufficient permissions for this operation")]
73 InsufficientPermissions,
74
75 #[error("token has been revoked")]
76 TokenRevoked,
77
78 #[error("internal server error during authentication, details: {0}")]
79 InternalError(#[serde(skip)] String),
80}
81
82impl From<jsonwebtoken::errors::Error> for AuthError {
83 fn from(value: jsonwebtoken::errors::Error) -> Self {
84 use jsonwebtoken::errors::ErrorKind::*;
85
86 match value.into_kind() {
87 ExpiredSignature => AuthError::TokenExpired,
88 InvalidSignature => AuthError::InvalidSignature,
89 InvalidIssuer => AuthError::InvalidIssuer,
90 InvalidAudience => AuthError::InvalidAudience,
91 InvalidSubject => AuthError::InvalidSubject,
92 MissingRequiredClaim(claim) => AuthError::MissingClaim(claim),
93 ImmatureSignature => AuthError::TokenNotYetValid,
94 InvalidToken => AuthError::InvalidToken,
95 Base64(e) => AuthError::InvalidBase64(e),
96 Utf8(e) => AuthError::InvalidUtf8(e),
97 Json(e) => AuthError::InvalidJson(e),
98
99 InvalidEcdsaKey => AuthError::InternalError("the secret given is not a valid ECDSA key".into()),
100 InvalidRsaKey(_) => AuthError::InternalError("the secret given is not a valid RSA key".into()),
101 RsaFailedSigning => AuthError::InternalError("could not sign with the given key".into()),
102 InvalidAlgorithmName => AuthError::InternalError("cannot parse algorithm from str".into()),
103 InvalidKeyFormat => AuthError::InternalError("a key is provided with an invalid format".into()),
104 InvalidAlgorithm => AuthError::InternalError("the algorithm in the header doesn't match the one passed to decode or the encoding/decoding key used doesn't match the alg requested".to_string()),
105 MissingAlgorithm => AuthError::InternalError("the Validation struct does not contain at least 1 algorithm".into()),
106 Crypto(e) => AuthError::InternalError(format!("Something unspecified went wrong with crypto: {e}")),
107 _ => todo!()
108 }
109 }
110}
111
112impl IntoResponse for AuthError {
113 fn into_response(self) -> Response {
114 let status_code = match self {
115 AuthError::MissingAuthHeader
116 | AuthError::InvalidKeyId
117 | AuthError::InvalidAuthFormat
118 | AuthError::InvalidToken
119 | AuthError::TokenExpired
120 | AuthError::TokenNotYetValid
121 | AuthError::InvalidAlgorithm(_)
122 | AuthError::InvalidSignature
123 | AuthError::InvalidIssuer
124 | AuthError::InvalidAudience
125 | AuthError::InvalidSubject
126 | AuthError::MissingClaim(_)
127 | AuthError::InvalidUtf8(_)
128 | AuthError::InvalidJson(_)
129 | AuthError::InvalidBase64(_)
130 | AuthError::TokenRevoked => StatusCode::UNAUTHORIZED,
131
132 AuthError::InsufficientPermissions => StatusCode::FORBIDDEN,
133
134 AuthError::InternalError(_) => StatusCode::UNAUTHORIZED,
135 };
136
137 status_code.into_response()
138 }
139}
140
141impl From<serde_json::Error> for AuthError {
142 fn from(value: serde_json::Error) -> Self {
143 Self::InvalidJson(Arc::new(value))
144 }
145}
146
147impl From<AuthError> for Response {
148 #[inline(always)]
149 fn from(val: AuthError) -> Response {
150 val.into_response()
151 }
152}