use std::sync::Arc; use axum::extract::{Request, State}; use axum::middleware::Next; use axum::response::Response; use rate_limit::AccessTokenBlacklist; use crate::handlers::JwtState; /// Services that own a [`JwtState`] can be passed to [`inject_jwt_ctx`], making /// `AuthUser`/`AuthOptional`/`AuthAdmin` extractors work in their handlers. pub trait HasJwtState { fn jwt_state(&self) -> &JwtState; /// Optional access-token deny-list. When present, the JWT extractors /// reject any token whose `jti` or user has been revoked (logout, /// password change, suspension). Defaults to `None` (no deny-check). fn access_blacklist(&self) -> Option> { None } } /// Middleware that injects the three pieces of context the JWT extractors /// require from `req.extensions_mut()`: /// /// 1. `Arc>` — signing key cache, /// 2. `JwtSettings` — token validation parameters, /// 3. `DatabaseConnection` — used to look up sessions during rotation. /// /// When the service implements [`HasJwtState::access_blacklist`], an /// `Option>` extension is injected as well. /// /// Apply to any router or route whose handlers depend on /// `jwt::extractor::{AuthUser, AuthOptional, AuthAdmin}`. pub async fn inject_jwt_ctx( State(svc): State, mut req: Request, next: Next, ) -> Response where S: HasJwtState + Send + Sync + Clone + 'static, { let jwt = svc.jwt_state(); req.extensions_mut().insert(jwt.cache.clone()); req.extensions_mut().insert(jwt.settings.clone()); req.extensions_mut().insert(jwt.db.clone()); req.extensions_mut().insert(svc.access_blacklist()); next.run(req).await }