Skip to content

Commit 8a73b4e

Browse files
committed
refactor: optimize JWT authentication with middleware caching
- Added auth_middleware to extract and cache JWT claims in request extensions, avoiding redundant token parsing - Updated Claims extractor to check extensions first before parsing token - Improved inline code rendering to prevent unwanted line breaks within code elements
1 parent 621326a commit 8a73b4e

File tree

5 files changed

+609
-12
lines changed

5 files changed

+609
-12
lines changed

backend/src/auth.rs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,14 +210,21 @@ pub fn build_cookie_removal() -> Cookie<'static> {
210210
/// Axum extractor for `Claims`.
211211
///
212212
/// This allows handlers to easily require authentication by including `Claims` in their arguments.
213-
/// It extracts the token from the `Authorization` header or the auth cookie.
213+
/// It first checks if claims are already in request extensions (from auth_middleware),
214+
/// otherwise extracts the token from the `Authorization` header or the auth cookie.
214215
impl<S> FromRequestParts<S> for Claims
215216
where
216217
S: Send + Sync,
217218
{
218219
type Rejection = (StatusCode, String);
219220

220221
async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
222+
// First check if claims are already in extensions (from auth_middleware)
223+
if let Some(claims) = parts.extensions.get::<Claims>() {
224+
return Ok(claims.clone());
225+
}
226+
227+
// Otherwise, extract and verify token
221228
let token = extract_token(&parts.headers).ok_or_else(|| {
222229
(
223230
StatusCode::UNAUTHORIZED,
@@ -350,3 +357,36 @@ fn parse_bearer_token(value: &str) -> Option<String> {
350357
}
351358
None
352359
}
360+
361+
/// Middleware that extracts JWT claims and inserts them into request extensions.
362+
///
363+
/// This allows other extractors (like CsrfGuard) to access the authenticated user's claims
364+
/// without re-parsing the JWT.
365+
///
366+
/// # Arguments
367+
///
368+
/// * `request` - The incoming request.
369+
/// * `next` - The next middleware/handler in the chain.
370+
///
371+
/// # Returns
372+
///
373+
/// The response, or a 401 error if authentication fails.
374+
pub async fn auth_middleware(
375+
mut request: axum::extract::Request,
376+
next: axum::middleware::Next,
377+
) -> Result<axum::response::Response, (StatusCode, String)> {
378+
let token = extract_token(request.headers()).ok_or_else(|| {
379+
(
380+
StatusCode::UNAUTHORIZED,
381+
"Missing authentication token".to_string(),
382+
)
383+
})?;
384+
385+
let claims = verify_jwt(&token)
386+
.map_err(|e| (StatusCode::UNAUTHORIZED, format!("Invalid token: {}", e)))?;
387+
388+
// Insert claims into request extensions so other extractors can access them
389+
request.extensions_mut().insert(claims);
390+
391+
Ok(next.run(request).await)
392+
}

backend/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ async fn main() {
367367
delete(handlers::comments::delete_comment),
368368
)
369369
.route_layer(middleware::from_extractor::<csrf::CsrfGuard>())
370-
.route_layer(middleware::from_extractor::<auth::Claims>())
370+
.route_layer(middleware::from_fn(auth::auth_middleware))
371371
.layer(RequestBodyLimitLayer::new(ADMIN_BODY_LIMIT))
372372
.layer(GovernorLayer::new(admin_rate_limit_config.clone()));
373373

0 commit comments

Comments
 (0)