diff --git a/jwt-authorizer/src/error.rs b/jwt-authorizer/src/error.rs index 5ad664c..6cf663f 100644 --- a/jwt-authorizer/src/error.rs +++ b/jwt-authorizer/src/error.rs @@ -55,6 +55,9 @@ pub enum AuthError { #[error("Invalid Claim")] InvalidClaims(), + + #[error("No Authorizer")] + NoAuthorizer(), } fn response_wwwauth(status: StatusCode, bearer: &str) -> Response { @@ -166,6 +169,10 @@ impl IntoResponse for AuthError { debug!("AuthErrors::InvalidClaims"); response_wwwauth(StatusCode::FORBIDDEN, "error=\"insufficient_scope\"") } + AuthError::NoAuthorizer() => { + debug!("AuthErrors::NoAuthorizer"); + response_wwwauth(StatusCode::FORBIDDEN, "error=\"invalid_token\"") + } } } } diff --git a/jwt-authorizer/src/layer.rs b/jwt-authorizer/src/layer.rs index e37f82c..6583868 100644 --- a/jwt-authorizer/src/layer.rs +++ b/jwt-authorizer/src/layer.rs @@ -3,6 +3,7 @@ use futures_core::ready; use futures_util::future::BoxFuture; use headers::authorization::Bearer; use headers::{Authorization, HeaderMapExt}; +use jsonwebtoken::TokenData; use pin_project::pin_project; use serde::de::DeserializeOwned; use std::future::Future; @@ -209,10 +210,9 @@ where type Future = BoxFuture<'static, Result, AuthError>>; fn authorize(&self, mut request: Request) -> Self::Future { - let authorizer = self.auth.clone(); + // TODO: extract token per authorizer (jwt_source shloud be per authorizer) let h = request.headers(); - - let token = match &self.jwt_source { + let token_o = match &self.jwt_source { layer::JwtSource::AuthorizationHeader => { let bearer_o: Option> = h.typed_get(); bearer_o.map(|b| String::from(b.0.token())) @@ -221,17 +221,30 @@ where .typed_get::() .and_then(|c| c.get(name.as_str()).map(String::from)), }; - Box::pin(async move { - if let Some(token) = token { - authorizer.check_auth(token.as_str()).await.map(|token_data| { - // Set `token_data` as a request extension so it can be accessed by other - // services down the stack. - request.extensions_mut().insert(token_data); - request - }) + let authorizers: Vec>> = self.auths.iter().map(|a| a.clone()).collect(); + + Box::pin(async move { + if let Some(token) = token_o { + let mut token_data: Result, AuthError> = Err(AuthError::NoAuthorizer()); + for auth in authorizers { + token_data = auth.check_auth(token.as_str()).await; + if token_data.is_ok() { + break; + } + } + match token_data { + Ok(tdata) => { + // Set `token_data` as a request extension so it can be accessed by other + // services down the stack. + request.extensions_mut().insert(tdata); + + return Ok(request); + } + Err(err) => return Err(err), // TODO: error containing all errors (not just the last one) + } } else { - Err(AuthError::MissingToken()) + return Err(AuthError::MissingToken()); } }) } @@ -291,7 +304,7 @@ where C: Clone + DeserializeOwned + Send + Sync, { pub inner: S, - pub auth: Arc>, + pub auths: Vec>>, pub jwt_source: JwtSource, } @@ -322,7 +335,11 @@ where /// /// The `Authorization` header is required to have the value provided. pub fn new(inner: S, auth: Arc>, jwt_source: JwtSource) -> AsyncAuthorizationService { - Self { inner, auth, jwt_source } + Self { + inner, + auths: vec![auth], + jwt_source, + } } }