rusttls_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    #[error("Failed to create HTTP client: {0}")]
67    ClientBuildError(#[from] reqwest::Error),
68}
69
70fn response_wwwauth(status: StatusCode, bearer: &str) -> Response<Body> {
71    let mut res = Response::new(Body::empty());
72    *res.status_mut() = status;
73    let h = if bearer.is_empty() {
74        "Bearer".to_owned()
75    } else {
76        format!("Bearer {bearer}")
77    };
78    res.headers_mut().insert(header::WWW_AUTHENTICATE, h.parse().unwrap());
79
80    res
81}
82
83fn response_500() -> Response<Body> {
84    let mut res = Response::new(Body::empty());
85    *res.status_mut() = StatusCode::INTERNAL_SERVER_ERROR;
86
87    res
88}
89
90#[cfg(feature = "tonic")]
91impl From<AuthError> for Response<tonic::body::BoxBody> {
92    fn from(e: AuthError) -> Self {
93        match e {
94            AuthError::JwksRefreshError(err) => {
95                tracing::error!("AuthErrors::JwksRefreshError: {}", err);
96                tonic::Status::internal("")
97            }
98            AuthError::InvalidKey(err) => {
99                tracing::error!("AuthErrors::InvalidKey: {}", err);
100                tonic::Status::internal("")
101            }
102            AuthError::JwksSerialisationError(err) => {
103                tracing::error!("AuthErrors::JwksSerialisationError: {}", err);
104                tonic::Status::internal("")
105            }
106            AuthError::InvalidKeyAlg(err) => {
107                debug!("AuthErrors::InvalidKeyAlg: {:?}", err);
108                tonic::Status::unauthenticated("error=\"invalid_token\", error_description=\"invalid key algorithm\"")
109            }
110            AuthError::InvalidKid(err) => {
111                debug!("AuthErrors::InvalidKid: {}", err);
112                tonic::Status::unauthenticated("error=\"invalid_token\", error_description=\"invalid kid\"")
113            }
114            AuthError::InvalidToken(err) => {
115                debug!("AuthErrors::InvalidToken: {}", err);
116                tonic::Status::unauthenticated("error=\"invalid_token\"")
117            }
118            AuthError::MissingToken() => {
119                debug!("AuthErrors::MissingToken");
120                tonic::Status::unauthenticated("")
121            }
122            AuthError::InvalidClaims() => {
123                debug!("AuthErrors::InvalidClaims");
124                tonic::Status::unauthenticated("error=\"insufficient_scope\"")
125            }
126            AuthError::NoAuthorizer() => {
127                debug!("AuthErrors::NoAuthorizer");
128                tonic::Status::unauthenticated("error=\"invalid_token\"")
129            }
130            AuthError::NoAuthorizerLayer() => {
131                debug!("AuthErrors::NoAuthorizerLayer");
132                tonic::Status::unauthenticated("error=\"no_authorizer_layer\"")
133            }
134        }
135        .into_http()
136    }
137}
138
139impl From<AuthError> for Response {
140    fn from(e: AuthError) -> Self {
141        e.into_response()
142    }
143}
144
145/// (<https://datatracker.ietf.org/doc/html/rfc6750#section-3.1>)
146impl IntoResponse for AuthError {
147    fn into_response(self) -> Response {
148        match self {
149            AuthError::JwksRefreshError(err) => {
150                tracing::error!("AuthErrors::JwksRefreshError: {}", err);
151                response_500()
152            }
153            AuthError::InvalidKey(err) => {
154                tracing::error!("AuthErrors::InvalidKey: {}", err);
155                response_500()
156            }
157            AuthError::JwksSerialisationError(err) => {
158                tracing::error!("AuthErrors::JwksSerialisationError: {}", err);
159                response_500()
160            }
161            AuthError::InvalidKeyAlg(err) => {
162                debug!("AuthErrors::InvalidKeyAlg: {:?}", err);
163                response_wwwauth(
164                    StatusCode::UNAUTHORIZED,
165                    "error=\"invalid_token\", error_description=\"invalid key algorithm\"",
166                )
167            }
168            AuthError::InvalidKid(err) => {
169                debug!("AuthErrors::InvalidKid: {}", err);
170                response_wwwauth(
171                    StatusCode::UNAUTHORIZED,
172                    "error=\"invalid_token\", error_description=\"invalid kid\"",
173                )
174            }
175            AuthError::InvalidToken(err) => {
176                debug!("AuthErrors::InvalidToken: {}", err);
177                response_wwwauth(StatusCode::UNAUTHORIZED, "error=\"invalid_token\"")
178            }
179            AuthError::MissingToken() => {
180                debug!("AuthErrors::MissingToken");
181                response_wwwauth(StatusCode::UNAUTHORIZED, "")
182            }
183            AuthError::InvalidClaims() => {
184                debug!("AuthErrors::InvalidClaims");
185                response_wwwauth(StatusCode::FORBIDDEN, "error=\"insufficient_scope\"")
186            }
187            AuthError::NoAuthorizer() => {
188                debug!("AuthErrors::NoAuthorizer");
189                response_wwwauth(StatusCode::FORBIDDEN, "error=\"invalid_token\"")
190            }
191            AuthError::NoAuthorizerLayer() => {
192                debug!("AuthErrors::NoAuthorizerLayer");
193                // TODO: should it be a standard error?
194                response_wwwauth(StatusCode::UNAUTHORIZED, "error=\"no_authorizer_layer\"")
195            }
196            AuthError::ClientBuildError(_) => {
197                debug!("AuthErrors::ClientBuildError");
198                response_500()
199            }
200        }
201    }
202}