@@ -26,15 +26,16 @@ use axum::{
2626 HeaderMap , HeaderValue , StatusCode ,
2727 } ,
2828 Json ,
29+ extract:: FromRef ,
2930} ;
3031use axum_extra:: extract:: cookie:: { Cookie , CookieJar , SameSite } ;
3132use chrono:: { Duration , Utc } ;
3233use jsonwebtoken:: { decode, encode, DecodingKey , EncodingKey , Header , Validation } ;
34+ use once_cell:: sync:: Lazy ;
3335use serde:: { Deserialize , Serialize } ;
34- use std:: collections:: HashSet ;
3536use std:: env;
36- use std :: sync :: OnceLock ;
37- use time :: { Duration as TimeDuration , OffsetDateTime } ;
37+
38+ use crate :: db :: { self , DbPool } ;
3839
3940/// Global storage for the JWT secret key.
4041/// Initialized once at application startup via init_jwt_secret().
@@ -357,10 +358,11 @@ pub fn build_cookie_removal() -> Cookie<'static> {
357358impl < S > FromRequestParts < S > for Claims
358359where
359360 S : Send + Sync ,
361+ DbPool : FromRef < S > ,
360362{
361363 type Rejection = ( StatusCode , String ) ;
362364
363- async fn from_request_parts ( parts : & mut Parts , _state : & S ) -> Result < Self , Self :: Rejection > {
365+ async fn from_request_parts ( parts : & mut Parts , state : & S ) -> Result < Self , Self :: Rejection > {
364366 // Check if claims already extracted by middleware
365367 if let Some ( claims) = parts. extensions . get :: < Claims > ( ) {
366368 return Ok ( claims. clone ( ) ) ;
@@ -378,6 +380,21 @@ where
378380 let claims = verify_jwt ( & token)
379381 . map_err ( |e| ( StatusCode :: UNAUTHORIZED , format ! ( "Invalid token: {}" , e) ) ) ?;
380382
383+ // Check if token is blacklisted
384+ let pool = DbPool :: from_ref ( state) ;
385+ if db:: is_token_blacklisted ( & pool, & token)
386+ . await
387+ . map_err ( |e| {
388+ tracing:: error!( "Database error checking token blacklist: {}" , e) ;
389+ (
390+ StatusCode :: INTERNAL_SERVER_ERROR ,
391+ "Internal server error" . to_string ( ) ,
392+ )
393+ } ) ?
394+ {
395+ return Err ( ( StatusCode :: UNAUTHORIZED , "Token has been revoked" . to_string ( ) ) ) ;
396+ }
397+
381398 Ok ( claims)
382399 }
383400}
@@ -495,7 +512,7 @@ pub fn cookies_should_be_secure() -> bool {
495512///
496513/// # Priority
497514/// Authorization header is checked first, falling back to cookies
498- fn extract_token ( headers : & HeaderMap ) -> Option < String > {
515+ pub fn extract_token ( headers : & HeaderMap ) -> Option < String > {
499516 // First check Authorization header
500517 if let Some ( header_value) = headers. get ( AUTHORIZATION ) {
501518 if let Ok ( value_str) = header_value. to_str ( ) {
@@ -536,60 +553,3 @@ fn parse_bearer_token(value: &str) -> Option<String> {
536553 None
537554}
538555
539- /// AXUM middleware for protecting routes with authentication.
540- ///
541- /// This middleware validates the JWT token and adds the claims to the
542- /// request extensions, making them available to downstream handlers.
543- ///
544- /// # Usage
545- /// ```rust,no_run
546- /// use axum::{Router, routing::get, middleware};
547- /// use linux_tutorial_cms::auth;
548- ///
549- /// let app = Router::new()
550- /// .route("/protected", get(handler))
551- /// .route_layer(middleware::from_fn(auth::auth_middleware));
552- /// ```
553- ///
554- /// # Authentication
555- /// Accepts tokens from:
556- /// - Authorization: Bearer <token> header
557- /// - ltcms_session cookie
558- ///
559- /// # Errors
560- /// Returns 401 Unauthorized if:
561- /// - No token provided
562- /// - Token is invalid or expired
563- ///
564- /// # Request Extensions
565- /// On success, inserts Claims into request extensions for easy access
566- /// by downstream handlers.
567- pub async fn auth_middleware (
568- mut request : axum:: extract:: Request ,
569- next : axum:: middleware:: Next ,
570- ) -> Result < axum:: response:: Response , ( StatusCode , Json < crate :: models:: ErrorResponse > ) > {
571- // Extract token from request
572- let token = extract_token ( request. headers ( ) ) . ok_or_else ( || {
573- (
574- StatusCode :: UNAUTHORIZED ,
575- Json ( crate :: models:: ErrorResponse {
576- error : "Missing authentication token" . to_string ( ) ,
577- } ) ,
578- )
579- } ) ?;
580-
581- // Verify token and extract claims
582- let claims = verify_jwt ( & token) . map_err ( |e| {
583- (
584- StatusCode :: UNAUTHORIZED ,
585- Json ( crate :: models:: ErrorResponse {
586- error : format ! ( "Invalid token: {}" , e) ,
587- } ) ,
588- )
589- } ) ?;
590-
591- // Add claims to request extensions for downstream handlers
592- request. extensions_mut ( ) . insert ( claims) ;
593-
594- Ok ( next. run ( request) . await )
595- }
0 commit comments