rust 71 lines · 9 steps

Custom auth extractors in Axum

Implementing FromRequestParts turns bearer-token validation into a reusable Axum handler argument.

Explained by highlit
1use axum::{
2 async_trait,
3 extract::{FromRequestParts, State},
4 http::{header, request::Parts, StatusCode},
5 response::{IntoResponse, Response},
6 Json,
7};
8use serde::Serialize;
9 
10pub struct AuthRejection {
11 status: StatusCode,
12 message: &'static str,
13}
14 
15impl IntoResponse for AuthRejection {
16 fn into_response(self) -> Response {
17 #[derive(Serialize)]
18 struct Body {
19 error: &'static str,
20 }
21 (self.status, Json(Body { error: self.message })).into_response()
22 }
23}
24 
25pub struct AuthUser {
26 pub id: i64,
27 pub scopes: Vec<String>,
28}
29 
30#[async_trait]
31impl FromRequestParts<AppState> for AuthUser {
32 type Rejection = AuthRejection;
33 
34 async fn from_request_parts(parts: &mut Parts, state: &AppState) -> Result<Self, Self::Rejection> {
35 let header = parts
36 .headers
37 .get(header::AUTHORIZATION)
38 .and_then(|v| v.to_str().ok())
39 .ok_or(AuthRejection {
40 status: StatusCode::UNAUTHORIZED,
41 message: "missing authorization header",
42 })?;
43 
44 let token = header.strip_prefix("Bearer ").ok_or(AuthRejection {
45 status: StatusCode::UNAUTHORIZED,
46 message: "expected bearer token",
47 })?;
48 
49 let claims = state.jwt.verify(token).map_err(|_| AuthRejection {
50 status: StatusCode::UNAUTHORIZED,
51 message: "invalid or expired token",
52 })?;
53 
54 state
55 .users
56 .find_active(claims.sub)
57 .await
58 .map_err(|_| AuthRejection {
59 status: StatusCode::INTERNAL_SERVER_ERROR,
60 message: "failed to load user",
61 })?
62 .ok_or(AuthRejection {
63 status: StatusCode::FORBIDDEN,
64 message: "account disabled",
65 })
66 .map(|record| AuthUser {
67 id: record.id,
68 scopes: claims.scopes,
69 })
70 }
71}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Implementing FromRequestParts lets handlers receive validated data as a plain typed argument.
  2. 2A dedicated rejection type with IntoResponse keeps each failure mapped to a precise status and message.
  3. 3The ? operator chains fallible steps so any auth failure short-circuits into a clean HTTP response.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Custom auth extractors in Axum — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code