#[cfg(feature = "jwt_auth")]
use axum::{
http::{Request, StatusCode, header::AUTHORIZATION},
response::{IntoResponse, Response},
};
#[cfg(feature = "jwt_auth")]
use futures::future::{self, BoxFuture};
#[cfg(feature = "jwt_auth")]
use jsonwebtoken::{Algorithm, DecodingKey, Validation, decode};
#[cfg(feature = "jwt_auth")]
use serde::de::DeserializeOwned;
#[cfg(feature = "jwt_auth")]
use std::{
convert::Infallible,
marker::PhantomData,
task::{Context, Poll},
};
#[cfg(feature = "jwt_auth")]
use tower::{Layer, Service};
use crate::security::jwt_auth::is_running_as_module;
use crate::security::secret_client::register_for_redaction;
#[cfg(all(feature = "jwt_auth", feature = "ipc"))]
use crate::security::jwt_auth::proxy_adapter::{JwtProxyConfig, JwtProxyService, validate_token_proxy};
#[cfg(all(feature = "jwt_auth", feature = "ipc"))]
use std::sync::Arc;
#[cfg(all(feature = "jwt_auth", feature = "ipc"))]
use tokio::sync::Mutex;
#[derive(Clone)]
pub struct JwtAuthenticationLayer<T = serde_json::Value> {
secret_key: String,
_marker: PhantomData<T>,
}
impl<T> JwtAuthenticationLayer<T> {
pub fn new(secret_key: String) -> Self {
register_for_redaction(&secret_key);
Self {
secret_key,
_marker: PhantomData,
}
}
pub fn with_validation(
self,
validation: Validation,
) -> JwtAuthenticationLayerWithValidation<T> {
JwtAuthenticationLayerWithValidation {
secret_key: self.secret_key,
validation,
_marker: PhantomData,
}
}
}
#[derive(Clone)]
pub struct JwtAuthenticationLayerWithValidation<T = serde_json::Value> {
secret_key: String,
validation: Validation,
_marker: PhantomData<T>,
}
impl<T> JwtAuthenticationLayerWithValidation<T> {
pub fn new(secret_key: String, validation: Validation) -> Self {
register_for_redaction(&secret_key);
Self {
secret_key,
validation,
_marker: PhantomData,
}
}
}
#[derive(Clone)]
pub struct JwtAuthService<S, T = serde_json::Value> {
inner: S,
secret_key: String,
validation: Validation,
_marker: PhantomData<T>,
#[cfg(feature = "ipc")]
proxy_service: Option<Arc<Mutex<Option<JwtProxyService>>>>,
}
impl<S, B, T> Service<Request<B>> for JwtAuthService<S, T>
where
S: Service<Request<B>, Response = Response, Error = Infallible> + Clone + Send + 'static,
S: 'static,
S::Future: Send + 'static,
B: Send + 'static,
T: DeserializeOwned + Send + Sync + Clone + 'static,
{
type Response = Response;
type Error = Infallible;
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx)
}
fn call(&mut self, mut req: Request<B>) -> Self::Future {
let key = self.secret_key.clone();
let validation = self.validation.clone();
let token_opt = req
.headers()
.get(AUTHORIZATION)
.and_then(|v| v.to_str().ok())
.and_then(|s| s.strip_prefix("Bearer "))
.map(str::to_string);
if let Some(token) = token_opt {
#[cfg(feature = "ipc")]
let proxy_service = self.proxy_service.clone();
let mut svc = self.inner.clone();
if is_running_as_module() {
#[cfg(feature = "ipc")]
{
Box::pin(async move {
let proxy_service_arc = match proxy_service {
Some(arc) => arc,
None => {
return Ok((
StatusCode::INTERNAL_SERVER_ERROR,
axum::Json(serde_json::json!({
"error": "JWT proxy service not initialized"
})),
)
.into_response());
}
};
let mut proxy_service_lock = proxy_service_arc.lock().await;
if proxy_service_lock.is_none() {
let config = JwtProxyConfig::default();
match JwtProxyService::connect(&config).await {
Ok(service) => {
*proxy_service_lock = Some(service);
}
Err(e) => {
return Ok((
StatusCode::INTERNAL_SERVER_ERROR,
axum::Json(serde_json::json!({
"error": format!("Failed to connect to JWT service: {}", e)
})),
)
.into_response());
}
}
}
let proxy = proxy_service_lock.as_ref().unwrap();
match validate_token_proxy::<T>(proxy, &token).await {
Ok(claims) => {
req.extensions_mut().insert(claims);
drop(proxy_service_lock); svc.call(req).await }
Err(err) => {
Ok((
StatusCode::UNAUTHORIZED,
axum::Json(serde_json::json!({
"error": format!("Unauthorized: {}", err)
})),
)
.into_response())
}
}
})
}
#[cfg(not(feature = "ipc"))]
{
Box::pin(future::ok(
(
StatusCode::INTERNAL_SERVER_ERROR,
axum::Json(serde_json::json!({
"error": "Running as a module but IPC feature is not enabled"
})),
)
.into_response(),
))
}
} else {
let decoding_key = DecodingKey::from_secret(key.as_bytes());
match decode::<T>(&token, &decoding_key, &validation) {
Ok(data) => {
req.extensions_mut().insert(data.claims);
Box::pin(async move { svc.call(req).await }) }
Err(err) => {
Box::pin(future::ok((
StatusCode::UNAUTHORIZED,
axum::Json(serde_json::json!({
"error": format!("Unauthorized: {}", err)
})),
)
.into_response()))
}
}
}
} else {
Box::pin(future::ok(
(
StatusCode::UNAUTHORIZED,
axum::Json(serde_json::json!({
"error": "Missing Authorization header"
})),
)
.into_response(),
))
}
}
}
impl<S, T> Layer<S> for JwtAuthenticationLayer<T> {
type Service = JwtAuthService<S, T>;
fn layer(&self, inner: S) -> Self::Service {
let mut validation = Validation::new(Algorithm::HS256);
validation.validate_exp = false;
JwtAuthService {
inner,
secret_key: self.secret_key.clone(),
validation,
_marker: PhantomData,
#[cfg(feature = "ipc")]
proxy_service: if is_running_as_module() {
Some(Arc::new(Mutex::new(None)))
} else {
None
},
}
}
}
impl<S, T> Layer<S> for JwtAuthenticationLayerWithValidation<T> {
type Service = JwtAuthService<S, T>;
fn layer(&self, inner: S) -> Self::Service {
JwtAuthService {
inner,
secret_key: self.secret_key.clone(),
validation: self.validation.clone(),
_marker: PhantomData,
#[cfg(feature = "ipc")]
proxy_service: if is_running_as_module() {
Some(Arc::new(Mutex::new(None)))
} else {
None
},
}
}
}