Skip to main content

axum_security/jwt/
session.rs

1use std::convert::Infallible;
2
3use axum::{
4    extract::{FromRequestParts, OptionalFromRequestParts},
5    http::{Extensions, StatusCode, request::Parts},
6};
7
8/// Decoded JWT claims extracted from request extensions.
9///
10/// Use as a handler parameter to require a valid JWT (returns `401` if missing).
11/// Use `Option<Jwt<T>>` for optional extraction.
12#[derive(Clone, Debug)]
13pub struct Jwt<T>(pub T);
14
15impl<T: Send + Sync + 'static> Jwt<T> {
16    pub fn from_extensions(extensions: &mut Extensions) -> Option<Self> {
17        extensions.remove()
18    }
19}
20
21impl<S, T> FromRequestParts<S> for Jwt<T>
22where
23    S: Send + Sync,
24    T: Send + Sync + 'static,
25{
26    type Rejection = StatusCode;
27
28    async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
29        if let Some(session) = <Jwt<T>>::from_extensions(&mut parts.extensions) {
30            Ok(session)
31        } else {
32            Err(StatusCode::UNAUTHORIZED)
33        }
34    }
35}
36
37impl<S, T> OptionalFromRequestParts<S> for Jwt<T>
38where
39    S: Send + Sync,
40    T: Send + Sync + 'static,
41{
42    type Rejection = Infallible;
43
44    async fn from_request_parts(
45        parts: &mut Parts,
46        _state: &S,
47    ) -> Result<Option<Self>, Self::Rejection> {
48        Ok(<Jwt<T>>::from_extensions(&mut parts.extensions))
49    }
50}
51
52#[cfg(test)]
53mod extract_jwt {
54    use axum::{
55        extract::FromRequestParts,
56        http::{Request, StatusCode},
57    };
58
59    use crate::jwt::Jwt;
60
61    #[tokio::test]
62    async fn extract() {
63        let jwt = Jwt(1i32);
64
65        let (mut parts, _) = Request::builder()
66            .extension(jwt.clone())
67            .body(())
68            .unwrap()
69            .into_parts();
70
71        let extracted_jwt = Jwt::<i32>::from_request_parts(&mut parts, &())
72            .await
73            .unwrap();
74
75        assert!(jwt.0 == extracted_jwt.0);
76    }
77
78    #[tokio::test]
79    async fn extract_rejection() {
80        let (mut parts, _) = Request::builder().body(()).unwrap().into_parts();
81
82        let rejection = Jwt::<i32>::from_request_parts(&mut parts, &())
83            .await
84            .unwrap_err();
85
86        assert!(rejection == StatusCode::UNAUTHORIZED);
87    }
88}