feat: multiple auths per AsyncAuthorizationService

This commit is contained in:
cduvray 2023-08-14 08:02:56 +02:00
parent 27cce24372
commit 57fbc6e399
2 changed files with 38 additions and 14 deletions

View file

@ -55,6 +55,9 @@ pub enum AuthError {
#[error("Invalid Claim")] #[error("Invalid Claim")]
InvalidClaims(), InvalidClaims(),
#[error("No Authorizer")]
NoAuthorizer(),
} }
fn response_wwwauth(status: StatusCode, bearer: &str) -> Response<BoxBody> { fn response_wwwauth(status: StatusCode, bearer: &str) -> Response<BoxBody> {
@ -166,6 +169,10 @@ impl IntoResponse for AuthError {
debug!("AuthErrors::InvalidClaims"); debug!("AuthErrors::InvalidClaims");
response_wwwauth(StatusCode::FORBIDDEN, "error=\"insufficient_scope\"") response_wwwauth(StatusCode::FORBIDDEN, "error=\"insufficient_scope\"")
} }
AuthError::NoAuthorizer() => {
debug!("AuthErrors::NoAuthorizer");
response_wwwauth(StatusCode::FORBIDDEN, "error=\"invalid_token\"")
}
} }
} }
} }

View file

@ -3,6 +3,7 @@ use futures_core::ready;
use futures_util::future::BoxFuture; use futures_util::future::BoxFuture;
use headers::authorization::Bearer; use headers::authorization::Bearer;
use headers::{Authorization, HeaderMapExt}; use headers::{Authorization, HeaderMapExt};
use jsonwebtoken::TokenData;
use pin_project::pin_project; use pin_project::pin_project;
use serde::de::DeserializeOwned; use serde::de::DeserializeOwned;
use std::future::Future; use std::future::Future;
@ -209,10 +210,9 @@ where
type Future = BoxFuture<'static, Result<Request<B>, AuthError>>; type Future = BoxFuture<'static, Result<Request<B>, AuthError>>;
fn authorize(&self, mut request: Request<B>) -> Self::Future { fn authorize(&self, mut request: Request<B>) -> Self::Future {
let authorizer = self.auth.clone(); // TODO: extract token per authorizer (jwt_source shloud be per authorizer)
let h = request.headers(); let h = request.headers();
let token_o = match &self.jwt_source {
let token = match &self.jwt_source {
layer::JwtSource::AuthorizationHeader => { layer::JwtSource::AuthorizationHeader => {
let bearer_o: Option<Authorization<Bearer>> = h.typed_get(); let bearer_o: Option<Authorization<Bearer>> = h.typed_get();
bearer_o.map(|b| String::from(b.0.token())) bearer_o.map(|b| String::from(b.0.token()))
@ -221,17 +221,30 @@ where
.typed_get::<headers::Cookie>() .typed_get::<headers::Cookie>()
.and_then(|c| c.get(name.as_str()).map(String::from)), .and_then(|c| c.get(name.as_str()).map(String::from)),
}; };
let authorizers: Vec<Arc<Authorizer<C>>> = self.auths.iter().map(|a| a.clone()).collect();
Box::pin(async move { Box::pin(async move {
if let Some(token) = token { if let Some(token) = token_o {
authorizer.check_auth(token.as_str()).await.map(|token_data| { let mut token_data: Result<TokenData<C>, 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 // 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(tdata);
request return Ok(request);
}) }
Err(err) => return Err(err), // TODO: error containing all errors (not just the last one)
}
} else { } else {
Err(AuthError::MissingToken()) return Err(AuthError::MissingToken());
} }
}) })
} }
@ -291,7 +304,7 @@ where
C: Clone + DeserializeOwned + Send + Sync, C: Clone + DeserializeOwned + Send + Sync,
{ {
pub inner: S, pub inner: S,
pub auth: Arc<Authorizer<C>>, pub auths: Vec<Arc<Authorizer<C>>>,
pub jwt_source: JwtSource, pub jwt_source: JwtSource,
} }
@ -322,7 +335,11 @@ where
/// ///
/// The `Authorization` header is required to have the value provided. /// The `Authorization` header is required to have the value provided.
pub fn new(inner: S, auth: Arc<Authorizer<C>>, jwt_source: JwtSource) -> AsyncAuthorizationService<S, C> { pub fn new(inner: S, auth: Arc<Authorizer<C>>, jwt_source: JwtSource) -> AsyncAuthorizationService<S, C> {
Self { inner, auth, jwt_source } Self {
inner,
auths: vec![auth],
jwt_source,
}
} }
} }