mirror of
https://github.com/TECHNOFAB11/jwt-authorizer.git
synced 2026-02-02 17:35:10 +01:00
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:
parent
783ed7e340
commit
9054f400dc
3 changed files with 76 additions and 11 deletions
29
Cargo.lock
generated
29
Cargo.lock
generated
|
|
@ -178,6 +178,17 @@ dependencies = [
|
||||||
"crossbeam-utils",
|
"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]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.9.3"
|
version = "0.9.3"
|
||||||
|
|
@ -740,6 +751,7 @@ dependencies = [
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower",
|
"tower",
|
||||||
|
"tower-cookies",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
"tower-layer",
|
"tower-layer",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
|
|
@ -1560,6 +1572,23 @@ dependencies = [
|
||||||
"tracing",
|
"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]]
|
[[package]]
|
||||||
name = "tower-http"
|
name = "tower-http"
|
||||||
version = "0.4.0"
|
version = "0.4.0"
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ tokio = { version = "1.25", features = ["full"] }
|
||||||
tower-http = { version = "0.4", features = ["trace", "auth"] }
|
tower-http = { version = "0.4", features = ["trace", "auth"] }
|
||||||
tower-layer = "0.3"
|
tower-layer = "0.3"
|
||||||
tower-service = "0.3"
|
tower-service = "0.3"
|
||||||
|
tower-cookies = "0.9.0"
|
||||||
tracing = "0.1"
|
tracing = "0.1"
|
||||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,13 @@ use std::sync::Arc;
|
||||||
use std::task::{Context, Poll};
|
use std::task::{Context, Poll};
|
||||||
use tower_layer::Layer;
|
use tower_layer::Layer;
|
||||||
use tower_service::Service;
|
use tower_service::Service;
|
||||||
|
use tower_cookies::Cookies;
|
||||||
|
|
||||||
use crate::authorizer::{Authorizer, FnClaimsChecker, KeySourceType};
|
use crate::authorizer::{Authorizer, FnClaimsChecker, KeySourceType};
|
||||||
use crate::error::InitError;
|
use crate::error::InitError;
|
||||||
use crate::jwks::key_store_manager::Refresh;
|
use crate::jwks::key_store_manager::Refresh;
|
||||||
use crate::validation::Validation;
|
use crate::validation::Validation;
|
||||||
use crate::{AuthError, RefreshStrategy};
|
use crate::{AuthError, layer, RefreshStrategy};
|
||||||
|
|
||||||
/// Authorizer Layer builder
|
/// Authorizer Layer builder
|
||||||
///
|
///
|
||||||
|
|
@ -32,6 +33,7 @@ where
|
||||||
refresh: Option<Refresh>,
|
refresh: Option<Refresh>,
|
||||||
claims_checker: Option<FnClaimsChecker<C>>,
|
claims_checker: Option<FnClaimsChecker<C>>,
|
||||||
validation: Option<Validation>,
|
validation: Option<Validation>,
|
||||||
|
pub jwt_source: JwtSource,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// authorization layer builder
|
/// authorization layer builder
|
||||||
|
|
@ -46,6 +48,7 @@ where
|
||||||
refresh: Default::default(),
|
refresh: Default::default(),
|
||||||
claims_checker: None,
|
claims_checker: None,
|
||||||
validation: None,
|
validation: None,
|
||||||
|
jwt_source: JwtSource::Bearer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,6 +59,7 @@ where
|
||||||
refresh: Default::default(),
|
refresh: Default::default(),
|
||||||
claims_checker: None,
|
claims_checker: None,
|
||||||
validation: None,
|
validation: None,
|
||||||
|
jwt_source: JwtSource::Bearer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -66,6 +70,7 @@ where
|
||||||
refresh: Default::default(),
|
refresh: Default::default(),
|
||||||
claims_checker: None,
|
claims_checker: None,
|
||||||
validation: None,
|
validation: None,
|
||||||
|
jwt_source: JwtSource::Bearer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -86,6 +91,7 @@ where
|
||||||
refresh: Default::default(),
|
refresh: Default::default(),
|
||||||
claims_checker: None,
|
claims_checker: None,
|
||||||
validation: None,
|
validation: None,
|
||||||
|
jwt_source: JwtSource::Bearer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -106,6 +112,7 @@ where
|
||||||
refresh: Default::default(),
|
refresh: Default::default(),
|
||||||
claims_checker: None,
|
claims_checker: None,
|
||||||
validation: None,
|
validation: None,
|
||||||
|
jwt_source: JwtSource::Bearer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -126,6 +133,7 @@ where
|
||||||
refresh: Default::default(),
|
refresh: Default::default(),
|
||||||
claims_checker: None,
|
claims_checker: None,
|
||||||
validation: None,
|
validation: None,
|
||||||
|
jwt_source: JwtSource::Bearer,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -164,14 +172,19 @@ where
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn jwt_source(mut self, src: JwtSource) -> JwtAuthorizer<C> {
|
||||||
|
self.jwt_source = src;
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
/// Build axum layer
|
/// Build axum layer
|
||||||
pub async fn layer(self) -> Result<AsyncAuthorizationLayer<C>, InitError> {
|
pub async fn layer(self) -> Result<AsyncAuthorizationLayer<C>, InitError> {
|
||||||
let val = self.validation.unwrap_or_default();
|
let val = self.validation.unwrap_or_default();
|
||||||
let auth = Arc::new(Authorizer::build(&self.key_source_type, self.claims_checker, self.refresh, val).await?);
|
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.
|
/// Trait for authorizing requests.
|
||||||
pub trait AsyncAuthorizer<B> {
|
pub trait AsyncAuthorizer<B> {
|
||||||
type RequestBody;
|
type RequestBody;
|
||||||
|
|
@ -196,10 +209,24 @@ where
|
||||||
fn authorize(&self, mut request: Request<B>) -> Self::Future {
|
fn authorize(&self, mut request: Request<B>) -> Self::Future {
|
||||||
let authorizer = self.auth.clone();
|
let authorizer = self.auth.clone();
|
||||||
let h = request.headers();
|
let h = request.headers();
|
||||||
|
|
||||||
|
let token = match &self.jwt_source {
|
||||||
|
layer::JwtSource::Bearer => {
|
||||||
let bearer_o : Option<Authorization<Bearer>> = h.typed_get();
|
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 {
|
Box::pin(async move {
|
||||||
if let Some(bearer) = bearer_o {
|
if let Some(token) = token {
|
||||||
match authorizer.check_auth(bearer.token()).await {
|
match authorizer.check_auth(token.as_str()).await {
|
||||||
Ok(token_data) => {
|
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.
|
||||||
|
|
@ -224,14 +251,15 @@ where
|
||||||
C: Clone + DeserializeOwned + Send,
|
C: Clone + DeserializeOwned + Send,
|
||||||
{
|
{
|
||||||
auth: Arc<Authorizer<C>>,
|
auth: Arc<Authorizer<C>>,
|
||||||
|
jwt_source: JwtSource,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<C> AsyncAuthorizationLayer<C>
|
impl<C> AsyncAuthorizationLayer<C>
|
||||||
where
|
where
|
||||||
C: Clone + DeserializeOwned + Send,
|
C: Clone + DeserializeOwned + Send,
|
||||||
{
|
{
|
||||||
pub fn new(auth: Arc<Authorizer<C>>) -> AsyncAuthorizationLayer<C> {
|
pub fn new(auth: Arc<Authorizer<C>>, jwt_source: JwtSource) -> AsyncAuthorizationLayer<C> {
|
||||||
Self { auth }
|
Self { auth, jwt_source }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -242,12 +270,18 @@ where
|
||||||
type Service = AsyncAuthorizationService<S, C>;
|
type Service = AsyncAuthorizationService<S, C>;
|
||||||
|
|
||||||
fn layer(&self, inner: S) -> Self::Service {
|
fn layer(&self, inner: S) -> Self::Service {
|
||||||
AsyncAuthorizationService::new(inner, self.auth.clone())
|
AsyncAuthorizationService::new(inner, self.auth.clone(), self.jwt_source.clone())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- AsyncAuthorizationService --------
|
// ---------- AsyncAuthorizationService --------
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum JwtSource{
|
||||||
|
Bearer,
|
||||||
|
Cookie(String),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct AsyncAuthorizationService<S, C>
|
pub struct AsyncAuthorizationService<S, C>
|
||||||
where
|
where
|
||||||
|
|
@ -255,6 +289,7 @@ where
|
||||||
{
|
{
|
||||||
pub inner: S,
|
pub inner: S,
|
||||||
pub auth: Arc<Authorizer<C>>,
|
pub auth: Arc<Authorizer<C>>,
|
||||||
|
pub jwt_source: JwtSource,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S, C> AsyncAuthorizationService<S, C>
|
impl<S, C> AsyncAuthorizationService<S, C>
|
||||||
|
|
@ -283,8 +318,8 @@ where
|
||||||
/// Authorize requests using a custom scheme.
|
/// Authorize requests using a custom scheme.
|
||||||
///
|
///
|
||||||
/// 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>>) -> AsyncAuthorizationService<S, C> {
|
pub fn new(inner: S, auth: Arc<Authorizer<C>>, jwt_source: JwtSource) -> AsyncAuthorizationService<S, C> {
|
||||||
Self { inner, auth }
|
Self { inner, auth , jwt_source }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue