axum_auth/
auth_bearer.rs

1//! Implementation of http bearer authentication
2//!
3//! See [AuthBearer] for the most commonly-used data structure
4
5use crate::{Rejection, ERR_CHARS, ERR_DEFAULT, ERR_MISSING, ERR_WRONG_BEARER};
6use axum_core::extract::FromRequestParts;
7use http::{header::AUTHORIZATION, request::Parts, StatusCode};
8
9/// Bearer token extractor which contains the innards of a bearer header as a string
10///
11/// This is enabled via the `auth-bearer` feature
12///
13/// # Example
14///
15/// This structure can be used like any other axum extractor:
16///
17/// ```no_run
18/// use axum_auth::AuthBearer;
19///
20/// /// Handler for a typical [axum] route, takes a `token` and returns it
21/// async fn handler(AuthBearer(token): AuthBearer) -> String {
22///     format!("Found a bearer token: {}", token)
23/// }
24/// ```
25///
26/// # Errors
27///
28/// There are a few errors which this extractor can make. By default, all invalid responses are `400 BAD REQUEST` with one of these messages:
29///
30/// - \`Authorization\` header must be a bearer token – Somebody tried to but basic auth here instead of bearer
31/// - \`Authorization\` header is missing – The header was required but it wasn't found
32/// - \`Authorization\` header contains invalid characters – The header couldn't be processed because of invalid characters
33#[derive(Debug, PartialEq, Eq, Clone)]
34pub struct AuthBearer(pub String);
35
36impl<B> FromRequestParts<B> for AuthBearer
37where
38    B: Send + Sync,
39{
40    type Rejection = Rejection;
41
42    async fn from_request_parts(req: &mut Parts, _: &B) -> Result<Self, Self::Rejection> {
43        Self::decode_request_parts(req)
44    }
45}
46
47impl AuthBearerCustom for AuthBearer {
48    const ERROR_CODE: StatusCode = ERR_DEFAULT;
49    const ERROR_OVERWRITE: Option<&'static str> = None;
50
51    fn from_header(contents: &str) -> Self {
52        Self(contents.to_string())
53    }
54}
55
56/// Custom extractor trait for bearer allowing you to implement custom responses
57///
58/// This is enabled via the `auth-bearer` feature
59///
60/// # Usage
61///
62/// To create your own bearer auth extractor using this crate, you have to:
63///
64/// 1. Make the extractor struct, something like `struct Example(String);`
65/// 2. Implement [FromRequestParts] that links to step 3, copy and paste this from the example below
66/// 3. Implement [AuthBearerCustom] to generate your extractor with your custom options, see the example below
67///
68/// Once you've completed these steps, you should have a new extractor which is just as easy to use as [AuthBearer] but has all of your custom configuration options inside of it!
69///
70/// # Example
71///
72/// This is what a typical custom extractor should look like in full, copy-paste this and edit it:
73///
74/// ```rust
75/// use axum::extract::FromRequestParts;
76/// use axum_auth::{AuthBearerCustom, Rejection};
77/// use http::{request::Parts, StatusCode};
78///
79/// /// Your custom bearer auth returning a fun 418 for errors
80/// struct MyCustomBearerAuth(String);
81///
82/// // This is where you define your custom options:
83/// impl AuthBearerCustom for MyCustomBearerAuth {
84///     const ERROR_CODE: StatusCode = StatusCode::IM_A_TEAPOT; // <-- define custom status code here
85///     const ERROR_OVERWRITE: Option<&'static str> = None; // <-- define overwriting message here
86///
87///     fn from_header(contents: &str) -> Self {
88///         Self(contents.to_string())
89///     }
90/// }
91///
92/// // This is boilerplate for now, copy and paste this:
93/// impl<B> FromRequestParts<B> for MyCustomBearerAuth
94/// where
95///     B: Send + Sync,
96/// {
97///     type Rejection = Rejection;
98///
99///     async fn from_request_parts(parts: &mut Parts, _: &B) -> Result<Self, Self::Rejection> {
100///         Self::decode_request_parts(parts)
101///     }
102/// }
103/// ```
104///
105/// Some notes about this example for some more insight:
106///
107/// - There's no reason for the [FromRequestParts] to ever change out of this pattern unless you're doing something special
108/// - It's recommended to use the `struct BearerExample(String);` pattern because it makes using it from routes easy
109pub trait AuthBearerCustom: Sized {
110    /// Error code to use instead of the typical `400 BAD REQUEST` error
111    const ERROR_CODE: StatusCode;
112
113    /// Message to overwrite all default ones with if required, leave as [None] ideally
114    const ERROR_OVERWRITE: Option<&'static str>;
115
116    /// Converts provided header contents to new instance of self; you need to implement this
117    ///
118    /// # Example
119    ///
120    /// With the typical `struct BearerExample(String);` pattern of structures, this can be implemented like so:
121    ///
122    /// ```rust
123    /// use axum_auth::AuthBearerCustom;
124    /// use http::StatusCode;
125    ///
126    /// struct BearerExample(String);
127    ///
128    /// impl AuthBearerCustom for BearerExample {
129    ///     const ERROR_CODE: StatusCode = StatusCode::BAD_REQUEST;
130    ///     const ERROR_OVERWRITE: Option<&'static str> = None;
131    ///
132    ///     fn from_header(contents: &str) -> Self {
133    ///         Self(contents.to_string())
134    ///     }
135    /// }
136    /// ```
137    ///
138    /// All this method does is let you put the automatically contents of the header into your resulting structure.
139    fn from_header(contents: &str) -> Self;
140
141    /// Decodes bearer token content into new instance of self from axum body parts; this is automatically implemented
142    fn decode_request_parts(req: &mut Parts) -> Result<Self, Rejection> {
143        // Get authorization header
144        let authorization = req
145            .headers
146            .get(AUTHORIZATION)
147            .ok_or((Self::ERROR_CODE, ERR_MISSING))?
148            .to_str()
149            .map_err(|_| (Self::ERROR_CODE, ERR_CHARS))?;
150
151        // Check that its a well-formed bearer and return
152        let split = authorization.split_once(' ');
153        match split {
154            // Found proper bearer
155            Some((name, contents)) if name == "Bearer" => Ok(Self::from_header(contents)),
156            // Found empty bearer; sometimes request libraries format them as this
157            _ if authorization == "Bearer" => Ok(Self::from_header("")),
158            // Found nothing
159            _ => Err((Self::ERROR_CODE, ERR_WRONG_BEARER)),
160        }
161    }
162}