Skip to main content

axum_security/
session.rs

1//! Unified session type across authentication methods.
2//!
3//! [`Session<U>`] is an enum that wraps the user type from whichever
4//! authentication layer ran: JWT, cookie, or basic auth. It implements
5//! [`Deref`](std::ops::Deref) to `U`, so you can access the inner user
6//! directly.
7//!
8//! This type is used internally by the [`rbac`](crate::rbac),
9//! [`pbac`](crate::pbac) module to work with any authentication method.
10//!
11//! You can also use it as a handler extractor when you support multiple
12//! auth methods and don't care which one was used.
13
14use axum::{
15    extract::FromRequestParts,
16    http::{Extensions, StatusCode, request::Parts},
17};
18
19/// A session extracted from request extensions, wrapping the authenticated user.
20///
21/// Variants are gated behind their respective features (`jwt`, `cookie`, `basic-auth`).
22/// Implements `Deref<Target = U>` for direct access to the inner user type.
23///
24/// Returns `401 Unauthorized` when used as a handler extractor if no session is present.
25#[derive(Clone)]
26pub enum Session<U> {
27    #[cfg(feature = "jwt")]
28    Jwt(crate::jwt::Jwt<U>),
29    #[cfg(feature = "cookie")]
30    Cookie(crate::cookie::CookieSession<U>),
31    #[cfg(feature = "basic-auth")]
32    Basic(crate::basic_auth::BasicAuth<U>),
33}
34
35impl<U: Clone + Send + Sync + 'static> Session<U> {
36    /// Extract a session from request extensions, trying each enabled auth method.
37    pub fn from_extensions(extensions: &mut Extensions) -> Option<Session<U>> {
38        #[cfg(feature = "jwt")]
39        if let Some(jwt) = extensions.remove::<crate::jwt::Jwt<U>>() {
40            return Some(Session::Jwt(jwt));
41        }
42
43        #[cfg(feature = "cookie")]
44        if let Some(c) = extensions.remove::<crate::cookie::CookieSession<U>>() {
45            return Some(Session::Cookie(c));
46        }
47
48        #[cfg(feature = "basic-auth")]
49        if let Some(b) = extensions.remove::<crate::basic_auth::BasicAuth<U>>() {
50            return Some(Session::Basic(b));
51        }
52
53        None
54    }
55
56    /// Re-insert this session into request extensions (restores the original variant).
57    pub fn insert_into(self, extensions: &mut Extensions) {
58        match self {
59            #[cfg(feature = "jwt")]
60            Self::Jwt(jwt) => {
61                extensions.insert(jwt);
62            }
63            #[cfg(feature = "cookie")]
64            Self::Cookie(c) => {
65                extensions.insert(c);
66            }
67            #[cfg(feature = "basic-auth")]
68            Self::Basic(b) => {
69                extensions.insert(b);
70            }
71        };
72    }
73}
74
75impl<S: Sync, U: Clone + Send + Sync + 'static> FromRequestParts<S> for Session<U> {
76    type Rejection = StatusCode;
77
78    async fn from_request_parts(parts: &mut Parts, _: &S) -> Result<Self, StatusCode> {
79        Session::from_extensions(&mut parts.extensions).ok_or(StatusCode::UNAUTHORIZED)
80    }
81}
82impl<U> std::ops::Deref for Session<U> {
83    type Target = U;
84
85    fn deref(&self) -> &U {
86        match self {
87            #[cfg(feature = "jwt")]
88            Self::Jwt(jwt) => &jwt.0,
89            #[cfg(feature = "cookie")]
90            Self::Cookie(c) => &c.state,
91            #[cfg(feature = "basic-auth")]
92            Self::Basic(b) => &b.0,
93        }
94    }
95}
96
97impl<U> std::ops::DerefMut for Session<U> {
98    fn deref_mut(&mut self) -> &mut Self::Target {
99        match self {
100            #[cfg(feature = "jwt")]
101            Self::Jwt(jwt) => &mut jwt.0,
102            #[cfg(feature = "cookie")]
103            Self::Cookie(c) => &mut c.state,
104            #[cfg(feature = "basic-auth")]
105            Self::Basic(b) => &mut b.0,
106        }
107    }
108}