diff --git a/jwt-authorizer/src/error.rs b/jwt-authorizer/src/error.rs index ad9bf4b..35b829e 100644 --- a/jwt-authorizer/src/error.rs +++ b/jwt-authorizer/src/error.rs @@ -58,6 +58,10 @@ pub enum AuthError { #[error("No Authorizer")] NoAuthorizer(), + + /// Used when a claim extractor is used and no authorization layer is in front the handler + #[error("No Authorizer Layer")] + NoAuthorizerLayer(), } fn response_wwwauth(status: StatusCode, bearer: &str) -> Response { @@ -120,6 +124,10 @@ impl From for Response { debug!("AuthErrors::NoAuthorizer"); tonic::Status::unauthenticated("error=\"invalid_token\"") } + AuthError::NoAuthorizerLayer() => { + debug!("AuthErrors::NoAuthorizerLayer"); + tonic::Status::unauthenticated("error=\"no_authorizer_layer\"") + } } .to_http() } @@ -177,6 +185,11 @@ impl IntoResponse for AuthError { debug!("AuthErrors::NoAuthorizer"); response_wwwauth(StatusCode::FORBIDDEN, "error=\"invalid_token\"") } + AuthError::NoAuthorizerLayer() => { + debug!("AuthErrors::NoAuthorizerLayer"); + // TODO: should it be a standard error? + response_wwwauth(StatusCode::UNAUTHORIZED, "error=\"no_authorizer_layer\"") + } } } } diff --git a/jwt-authorizer/src/lib.rs b/jwt-authorizer/src/lib.rs index 2417386..1940777 100644 --- a/jwt-authorizer/src/lib.rs +++ b/jwt-authorizer/src/lib.rs @@ -1,7 +1,6 @@ #![doc = include_str!("../docs/README.md")] use axum::{async_trait, extract::FromRequestParts, http::request::Parts}; -use http::StatusCode; use jsonwebtoken::TokenData; use serde::de::DeserializeOwned; @@ -30,14 +29,13 @@ where T: DeserializeOwned + Send + Sync + Clone + 'static, S: Send + Sync, { - type Rejection = StatusCode; + type Rejection = AuthError; async fn from_request_parts(parts: &mut Parts, _: &S) -> Result { if let Some(claims) = parts.extensions.get::>() { Ok(JwtClaims(claims.claims.clone())) } else { - tracing::error!("JwtClaims extractor must be behind a jwt-authoriser layer!"); - Err(StatusCode::INTERNAL_SERVER_ERROR) + Err(AuthError::NoAuthorizerLayer()) } } } diff --git a/jwt-authorizer/tests/tests.rs b/jwt-authorizer/tests/tests.rs index a1ca239..3f99917 100644 --- a/jwt-authorizer/tests/tests.rs +++ b/jwt-authorizer/tests/tests.rs @@ -162,7 +162,7 @@ mod tests { } #[tokio::test] - async fn extract_from_public_500() { + async fn extract_from_public_401() { let app = Router::new().route( "/public", get(|JwtClaims(user): JwtClaims| async move { format!("hello: {}", user.sub) }), @@ -172,7 +172,23 @@ mod tests { .await .unwrap(); - assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR); + assert_eq!(response.status(), StatusCode::UNAUTHORIZED); + } + + #[tokio::test] + async fn extract_from_public_optional() { + let app = Router::new().route( + "/public", + get(|user: Option>| async move { format!("option: {}", user.is_none()) }), + ); + let response = app + .oneshot(Request::builder().uri("/public").body(Body::empty()).unwrap()) + .await + .unwrap(); + + assert_eq!(response.status(), StatusCode::OK); + let body = hyper::body::to_bytes(response.into_body()).await.unwrap(); + assert_eq!(&body[..], b"option: true"); } // --------------------