jwt_authorizer/
error.rs

1use axum::{
2    body::Body,
3    http::StatusCode,
4    response::{IntoResponse, Response},
5};
6use http::header;
7use jsonwebtoken::Algorithm;
8use thiserror::Error;
9
10use tracing::debug;
11
12#[derive(Debug, Error)]
13pub enum InitError {
14    #[error("Builder Error {0}")]
15    BuilderError(String),
16
17    #[error(transparent)]
18    KeyFileError(#[from] std::io::Error),
19
20    #[error(transparent)]
21    KeyDecodingError(#[from] jsonwebtoken::errors::Error),
22
23    #[error("Builder Error {0}")]
24    DiscoveryError(String),
25
26    #[error("Builder Error {0}")]
27    JwksUrlError(String),
28
29    #[error("Jwks Parsing Error {0}")]
30    JwksParsingError(#[from] serde_json::Error),
31}
32
33#[derive(Debug, Error)]
34pub enum AuthError {
35    #[error(transparent)]
36    JwksSerialisationError(#[from] serde_json::Error),
37
38    #[error("JwksRefreshError {0}")]
39    JwksRefreshError(String),
40
41    #[error("InvalidKey {0}")]
42    InvalidKey(String),
43
44    #[error("Invalid Kid {0}")]
45    InvalidKid(String),
46
47    #[error("Invalid Key Algorithm {0:?}")]
48    InvalidKeyAlg(Algorithm),
49
50    #[error("Missing Token")]
51    MissingToken(),
52
53    #[error(transparent)]
54    InvalidToken(#[from] jsonwebtoken::errors::Error),
55
56    #[error("Invalid Claim")]
57    InvalidClaims(),
58
59    #[error("No Authorizer")]
60    NoAuthorizer(),
61
62    /// Used when a claim extractor is used and no authorization layer is in front the handler
63    #[error("No Authorizer Layer")]
64    NoAuthorizerLayer(),
65}
66
67fn response_wwwauth(status: StatusCode, bearer: &str) -> Response<Body> {
68    let mut res = Response::new(Body::empty());
69    *res.status_mut() = status;
70    let h = if bearer.is_empty() {
71        "Bearer".to_owned()
72    } else {
73        format!("Bearer {bearer}")
74    };
75    res.headers_mut().insert(header::WWW_AUTHENTICATE, h.parse().unwrap());
76
77    res
78}
79
80fn response_500() -> Response<Body> {
81    let mut res = Response::new(Body::empty());
82    *res.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
83
84    res
85}
86
87#[cfg(feature = "tonic")]
88impl From<AuthError> for Response<tonic::body::BoxBody> {
89    fn from(e: AuthError) -> Self {
90        match e {
91            AuthError::JwksRefreshError(err) => {
92                tracing::error!("AuthErrors::JwksRefreshError: {}", err);
93                tonic::Status::internal("")
94            }
95            AuthError::InvalidKey(err) => {
96                tracing::error!("AuthErrors::InvalidKey: {}", err);
97                tonic::Status::internal("")
98            }
99            AuthError::JwksSerialisationError(err) => {
100                tracing::error!("AuthErrors::JwksSerialisationError: {}", err);
101                tonic::Status::internal("")
102            }
103            AuthError::InvalidKeyAlg(err) => {
104                debug!("AuthErrors::InvalidKeyAlg: {:?}", err);
105                tonic::Status::unauthenticated("error=\"invalid_token\", error_description=\"invalid key algorithm\"")
106            }
107            AuthError::InvalidKid(err) => {
108                debug!("AuthErrors::InvalidKid: {}", err);
109                tonic::Status::unauthenticated("error=\"invalid_token\", error_description=\"invalid kid\"")
110            }
111            AuthError::InvalidToken(err) => {
112                debug!("AuthErrors::InvalidToken: {}", err);
113                tonic::Status::unauthenticated("error=\"invalid_token\"")
114            }
115            AuthError::MissingToken() => {
116                debug!("AuthErrors::MissingToken");
117                tonic::Status::unauthenticated("")
118            }
119            AuthError::InvalidClaims() => {
120                debug!("AuthErrors::InvalidClaims");
121                tonic::Status::unauthenticated("error=\"insufficient_scope\"")
122            }
123            AuthError::NoAuthorizer() => {
124                debug!("AuthErrors::NoAuthorizer");
125                tonic::Status::unauthenticated("error=\"invalid_token\"")
126            }
127            AuthError::NoAuthorizerLayer() => {
128                debug!("AuthErrors::NoAuthorizerLayer");
129                tonic::Status::unauthenticated("error=\"no_authorizer_layer\"")
130            }
131        }
132        .into_http()
133    }
134}
135
136impl From<AuthError> for Response {
137    fn from(e: AuthError) -> Self {
138        e.into_response()
139    }
140}
141
142/// (<https://datatracker.ietf.org/doc/html/rfc6750#section-3.1>)
143impl IntoResponse for AuthError {
144    fn into_response(self) -> Response {
145        match self {
146            AuthError::JwksRefreshError(err) => {
147                tracing::error!("AuthErrors::JwksRefreshError: {}", err);
148                response_500()
149            }
150            AuthError::InvalidKey(err) => {
151                tracing::error!("AuthErrors::InvalidKey: {}", err);
152                response_500()
153            }
154            AuthError::JwksSerialisationError(err) => {
155                tracing::error!("AuthErrors::JwksSerialisationError: {}", err);
156                response_500()
157            }
158            AuthError::InvalidKeyAlg(err) => {
159                debug!("AuthErrors::InvalidKeyAlg: {:?}", err);
160                response_wwwauth(
161                    StatusCode::UNAUTHORIZED,
162                    "error=\"invalid_token\", error_description=\"invalid key algorithm\"",
163                )
164            }
165            AuthError::InvalidKid(err) => {
166                debug!("AuthErrors::InvalidKid: {}", err);
167                response_wwwauth(
168                    StatusCode::UNAUTHORIZED,
169                    "error=\"invalid_token\", error_description=\"invalid kid\"",
170                )
171            }
172            AuthError::InvalidToken(err) => {
173                debug!("AuthErrors::InvalidToken: {}", err);
174                response_wwwauth(StatusCode::UNAUTHORIZED, "error=\"invalid_token\"")
175            }
176            AuthError::MissingToken() => {
177                debug!("AuthErrors::MissingToken");
178                response_wwwauth(StatusCode::UNAUTHORIZED, "")
179            }
180            AuthError::InvalidClaims() => {
181                debug!("AuthErrors::InvalidClaims");
182                response_wwwauth(StatusCode::FORBIDDEN, "error=\"insufficient_scope\"")
183            }
184            AuthError::NoAuthorizer() => {
185                debug!("AuthErrors::NoAuthorizer");
186                response_wwwauth(StatusCode::FORBIDDEN, "error=\"invalid_token\"")
187            }
188            AuthError::NoAuthorizerLayer() => {
189                debug!("AuthErrors::NoAuthorizerLayer");
190                // TODO: should it be a standard error?
191                response_wwwauth(StatusCode::UNAUTHORIZED, "error=\"no_authorizer_layer\"")
192            }
193        }
194    }
195}