feat: implementation of named cookie as jwt source (#10)

* feat: working but naive implementation of named cookie as jwt source

* refactor: add with_jwt_source to JwtAuthorizer. Make Bearer default

* fix: fix the demo-server. Remove JWTSource

* refactor: rename with_jwt_source() -> jwt_source()
This commit is contained in:
Felix B. Bause 2023-03-30 07:25:39 +02:00 committed by GitHub
parent 783ed7e340
commit 9054f400dc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 76 additions and 11 deletions

29
Cargo.lock generated
View file

@ -178,6 +178,17 @@ dependencies = [
"crossbeam-utils",
]
[[package]]
name = "cookie"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7efb37c3e1ccb1ff97164ad95ac1606e8ccd35b3fa0a7d99a304c7f4a428cc24"
dependencies = [
"percent-encoding",
"time",
"version_check",
]
[[package]]
name = "core-foundation"
version = "0.9.3"
@ -740,6 +751,7 @@ dependencies = [
"thiserror",
"tokio",
"tower",
"tower-cookies",
"tower-http",
"tower-layer",
"tower-service",
@ -1560,6 +1572,23 @@ dependencies = [
"tracing",
]
[[package]]
name = "tower-cookies"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40f38d941a2ffd8402b36e02ae407637a9caceb693aaf2edc910437db0f36984"
dependencies = [
"async-trait",
"axum-core",
"cookie",
"futures-util",
"http",
"parking_lot",
"pin-project-lite",
"tower-layer",
"tower-service",
]
[[package]]
name = "tower-http"
version = "0.4.0"

View file

@ -25,6 +25,7 @@ tokio = { version = "1.25", features = ["full"] }
tower-http = { version = "0.4", features = ["trace", "auth"] }
tower-layer = "0.3"
tower-service = "0.3"
tower-cookies = "0.9.0"
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }

View file

@ -13,12 +13,13 @@ use std::sync::Arc;
use std::task::{Context, Poll};
use tower_layer::Layer;
use tower_service::Service;
use tower_cookies::Cookies;
use crate::authorizer::{Authorizer, FnClaimsChecker, KeySourceType};
use crate::error::InitError;
use crate::jwks::key_store_manager::Refresh;
use crate::validation::Validation;
use crate::{AuthError, RefreshStrategy};
use crate::{AuthError, layer, RefreshStrategy};
/// Authorizer Layer builder
///
@ -32,6 +33,7 @@ where
refresh: Option<Refresh>,
claims_checker: Option<FnClaimsChecker<C>>,
validation: Option<Validation>,
pub jwt_source: JwtSource,
}
/// authorization layer builder
@ -46,6 +48,7 @@ where
refresh: Default::default(),
claims_checker: None,
validation: None,
jwt_source: JwtSource::Bearer,
}
}
@ -56,6 +59,7 @@ where
refresh: Default::default(),
claims_checker: None,
validation: None,
jwt_source: JwtSource::Bearer,
}
}
@ -66,6 +70,7 @@ where
refresh: Default::default(),
claims_checker: None,
validation: None,
jwt_source: JwtSource::Bearer,
}
}
@ -86,6 +91,7 @@ where
refresh: Default::default(),
claims_checker: None,
validation: None,
jwt_source: JwtSource::Bearer,
}
}
@ -106,6 +112,7 @@ where
refresh: Default::default(),
claims_checker: None,
validation: None,
jwt_source: JwtSource::Bearer,
}
}
@ -126,6 +133,7 @@ where
refresh: Default::default(),
claims_checker: None,
validation: None,
jwt_source: JwtSource::Bearer,
}
}
@ -164,14 +172,19 @@ where
self
}
pub fn jwt_source(mut self, src: JwtSource) -> JwtAuthorizer<C> {
self.jwt_source = src;
self
}
/// Build axum layer
pub async fn layer(self) -> Result<AsyncAuthorizationLayer<C>, InitError> {
let val = self.validation.unwrap_or_default();
let auth = Arc::new(Authorizer::build(&self.key_source_type, self.claims_checker, self.refresh, val).await?);
Ok(AsyncAuthorizationLayer::new(auth))
Ok(AsyncAuthorizationLayer::new(auth, self.jwt_source))
}
}
/// Trait for authorizing requests.
pub trait AsyncAuthorizer<B> {
type RequestBody;
@ -196,10 +209,24 @@ where
fn authorize(&self, mut request: Request<B>) -> Self::Future {
let authorizer = self.auth.clone();
let h = request.headers();
let bearer_o: Option<Authorization<Bearer>> = h.typed_get();
let token = match &self.jwt_source {
layer::JwtSource::Bearer => {
let bearer_o : Option<Authorization<Bearer>> = h.typed_get();
bearer_o.and_then(|b| Some(String::from(b.0.token())))
}
layer::JwtSource::Cookie(name) => {
if let Some(c) = request.extensions().get::<Cookies>() {
c.get(name.as_str()).and_then(|c| Some(String::from(c.value())))
} else {
tracing::warn!("You have to add the tower_cookies::CookieManagerLayer middleware to use Cookies as JWT source.");
None
}
}
};
Box::pin(async move {
if let Some(bearer) = bearer_o {
match authorizer.check_auth(bearer.token()).await {
if let Some(token) = token {
match authorizer.check_auth(token.as_str()).await {
Ok(token_data) => {
// Set `token_data` as a request extension so it can be accessed by other
// services down the stack.
@ -224,14 +251,15 @@ where
C: Clone + DeserializeOwned + Send,
{
auth: Arc<Authorizer<C>>,
jwt_source: JwtSource,
}
impl<C> AsyncAuthorizationLayer<C>
where
C: Clone + DeserializeOwned + Send,
{
pub fn new(auth: Arc<Authorizer<C>>) -> AsyncAuthorizationLayer<C> {
Self { auth }
pub fn new(auth: Arc<Authorizer<C>>, jwt_source: JwtSource) -> AsyncAuthorizationLayer<C> {
Self { auth, jwt_source }
}
}
@ -242,12 +270,18 @@ where
type Service = AsyncAuthorizationService<S, C>;
fn layer(&self, inner: S) -> Self::Service {
AsyncAuthorizationService::new(inner, self.auth.clone())
AsyncAuthorizationService::new(inner, self.auth.clone(), self.jwt_source.clone())
}
}
// ---------- AsyncAuthorizationService --------
#[derive(Clone)]
pub enum JwtSource{
Bearer,
Cookie(String),
}
#[derive(Clone)]
pub struct AsyncAuthorizationService<S, C>
where
@ -255,6 +289,7 @@ where
{
pub inner: S,
pub auth: Arc<Authorizer<C>>,
pub jwt_source: JwtSource,
}
impl<S, C> AsyncAuthorizationService<S, C>
@ -283,8 +318,8 @@ where
/// Authorize requests using a custom scheme.
///
/// The `Authorization` header is required to have the value provided.
pub fn new(inner: S, auth: Arc<Authorizer<C>>) -> AsyncAuthorizationService<S, C> {
Self { inner, auth }
pub fn new(inner: S, auth: Arc<Authorizer<C>>, jwt_source: JwtSource) -> AsyncAuthorizationService<S, C> {
Self { inner, auth , jwt_source }
}
}