fix: jwks store unavailable should be error 500

- fix
- 403 -> 401 for some errors
- errors refactoring
This commit is contained in:
cduvray 2023-01-16 23:32:51 +01:00
parent 4c696471de
commit af29f39656
3 changed files with 25 additions and 34 deletions

View file

@ -1,5 +1,4 @@
use axum::{ use axum::{
extract::rejection::TypedHeaderRejection,
http::StatusCode, http::StatusCode,
response::{IntoResponse, Response}, response::{IntoResponse, Response},
}; };
@ -37,8 +36,8 @@ pub enum AuthError {
#[error("Invalid Key Algorithm {0:?}")] #[error("Invalid Key Algorithm {0:?}")]
InvalidKeyAlg(Algorithm), InvalidKeyAlg(Algorithm),
#[error(transparent)] // #[error(transparent)]
InvalidTokenHeader(#[from] TypedHeaderRejection), // InvalidTokenHeader(#[from] TypedHeaderRejection),
#[error(transparent)] #[error(transparent)]
InvalidToken(#[from] jsonwebtoken::errors::Error), InvalidToken(#[from] jsonwebtoken::errors::Error),
@ -49,16 +48,17 @@ pub enum AuthError {
impl IntoResponse for AuthError { impl IntoResponse for AuthError {
fn into_response(self) -> Response { fn into_response(self) -> Response {
// TODO: add error code (https://datatracker.ietf.org/doc/html/rfc6750#section-3.1)
warn!("AuthError: {}", &self); warn!("AuthError: {}", &self);
let (status, error_message) = match self { let (status, error_message) = match self {
AuthError::JwksRefreshError(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()), AuthError::JwksRefreshError(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()),
AuthError::InvalidKid(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg), AuthError::InvalidKid(msg) => (StatusCode::UNAUTHORIZED, msg),
AuthError::InvalidTokenHeader(_) => (StatusCode::BAD_REQUEST, self.to_string()), // AuthError::InvalidTokenHeader(_) => (StatusCode::BAD_REQUEST, self.to_string()),
AuthError::InvalidToken(_) => (StatusCode::BAD_REQUEST, self.to_string()), AuthError::InvalidToken(_) => (StatusCode::UNAUTHORIZED, self.to_string()),
AuthError::InvalidKey(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg), AuthError::InvalidKey(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg),
AuthError::JwksSerialisationError(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()), AuthError::JwksSerialisationError(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()),
AuthError::InvalidKeyAlg(_) => (StatusCode::BAD_REQUEST, self.to_string()), AuthError::InvalidKeyAlg(_) => (StatusCode::BAD_REQUEST, self.to_string()),
AuthError::InvalidClaims() => (StatusCode::FORBIDDEN, self.to_string()), AuthError::InvalidClaims() => (StatusCode::UNAUTHORIZED, self.to_string()),
}; };
let body = axum::Json(serde_json::json!({ let body = axum::Json(serde_json::json!({
"error": error_message, "error": error_message,

View file

@ -1,6 +1,6 @@
use axum::body::BoxBody;
use axum::http::Request; use axum::http::Request;
use axum::response::IntoResponse; use axum::response::{Response, IntoResponse};
use axum::{body::Body, response::Response};
use futures_core::ready; use futures_core::ready;
use futures_util::future::BoxFuture; use futures_util::future::BoxFuture;
use headers::authorization::Bearer; use headers::authorization::Bearer;
@ -123,7 +123,7 @@ where
C: Clone + DeserializeOwned + Send + Sync + 'static, C: Clone + DeserializeOwned + Send + Sync + 'static,
{ {
type RequestBody = B; type RequestBody = B;
type ResponseBody = Body; type ResponseBody = BoxBody;
type Future = BoxFuture<'static, Result<Request<B>, Response<Self::ResponseBody>>>; type Future = BoxFuture<'static, Result<Request<B>, Response<Self::ResponseBody>>>;
fn authorize(&self, mut request: Request<B>) -> Self::Future { fn authorize(&self, mut request: Request<B>) -> Self::Future {
@ -132,27 +132,20 @@ where
let bearer_o: Option<Authorization<Bearer>> = h.typed_get(); let bearer_o: Option<Authorization<Bearer>> = h.typed_get();
Box::pin(async move { Box::pin(async move {
if let Some(bearer) = bearer_o { if let Some(bearer) = bearer_o {
if let Ok(token_data) = authorizer.check_auth(bearer.token()).await { match authorizer.check_auth(bearer.token()).await {
Ok(token_data) => {
// Set `token_data` as a request extension so it can be accessed by other // Set `token_data` as a request extension so it can be accessed by other
// services down the stack. // services down the stack.
request.extensions_mut().insert(token_data); request.extensions_mut().insert(token_data);
Ok(request) Ok(request)
} else { },
let unauthorized_response = Response::builder() Err(err) => {
.status(StatusCode::UNAUTHORIZED) Err(err.into_response())
.body(Body::empty()) // TODO: add error code (https://datatracker.ietf.org/doc/html/rfc6750#section-3.1) }
.unwrap();
Err(unauthorized_response)
} }
} else { } else {
let unauthorized_response = Response::builder() Err((StatusCode::UNAUTHORIZED).into_response())
.status(StatusCode::UNAUTHORIZED)
.body(Body::empty()) // TODO: add error message (https://datatracker.ietf.org/doc/html/rfc6750#section-3.1)
.unwrap();
Err(unauthorized_response)
} }
}) })
} }
@ -302,9 +295,7 @@ where
} }
Err(res) => { Err(res) => {
tracing::info!("err: {:?}", res); tracing::info!("err: {:?}", res);
let r = (StatusCode::FORBIDDEN, format!("Unauthorized : {:?}", res)).into_response(); return Poll::Ready(Ok(res));
// TODO: replace r by res (type problems: res should be already a 403 error response)
return Poll::Ready(Ok(r));
} }
}; };
} }

View file

@ -47,7 +47,7 @@ mod tests {
.await .await
.unwrap(); .unwrap();
assert_eq!(response.status(), StatusCode::FORBIDDEN); assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
// TODO: check error code (https://datatracker.ietf.org/doc/html/rfc6750#section-3.1) // TODO: check error code (https://datatracker.ietf.org/doc/html/rfc6750#section-3.1)
} }
@ -73,7 +73,7 @@ mod tests {
"xxx.xxx.xxx" "xxx.xxx.xxx"
).await; ).await;
assert_eq!(response.status(), StatusCode::FORBIDDEN); assert_eq!(response.status(), StatusCode::UNAUTHORIZED);
// TODO: check error code (https://datatracker.ietf.org/doc/html/rfc6750#section-3.1) // TODO: check error code (https://datatracker.ietf.org/doc/html/rfc6750#section-3.1)
} }