Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Route Protection

This chapter covers different methods to protect routes in your Axum application.

Method 1: AuthUser Extractor (Simplest)

Use AuthUser as an extractor. Unauthenticated users are redirected to login.

use oauth2_passkey_axum::AuthUser;

// Requires authentication - redirects to login if not authenticated
async fn dashboard(user: AuthUser) -> String {
    format!("Welcome, {}! (user_id: {})", user.label, user.user_id)
}

// AuthUser fields:
// - user_id: Unique user identifier
// - label: Display name
// - account: Account identifier
// - csrf_token: CSRF token for forms
// - is_admin: Whether user has admin privileges

Method 2: Option<AuthUser> (Optional Authentication)

Allow both authenticated and anonymous users:

use oauth2_passkey_axum::AuthUser;

// Works for both authenticated and anonymous users
async fn public_page(user: Option<AuthUser>) -> String {
    match user {
        Some(u) => format!("Hello, {}!", u.label),
        None => "Hello, Anonymous!".to_string(),
    }
}

Method 3: Middleware (Route-Layer Protection)

Apply middleware to protect entire routes or groups:

use axum::{Router, Extension, middleware::from_fn, routing::get};
use oauth2_passkey_axum::{
    is_authenticated_redirect,      // Redirects to login
    is_authenticated_user_redirect, // Redirects + provides AuthUser via Extension
    is_authenticated_401,           // Returns 401 (for APIs)
    AuthUser, CsrfToken,
};

let app = Router::new()
    // Method 3a: Middleware only (no user info needed)
    .route("/protected", get(protected_page)
        .route_layer(from_fn(is_authenticated_redirect)))

    // Method 3b: Middleware + user info via Extension
    .route("/dashboard", get(dashboard_with_user)
        .route_layer(from_fn(is_authenticated_user_redirect)))

    // Method 3c: Protect entire nested router
    .nest("/admin", admin_router()
        .route_layer(from_fn(is_authenticated_redirect)));

// With is_authenticated_redirect: no user argument needed
async fn protected_page() -> String {
    "Protected content".to_string()
}

// With is_authenticated_user_redirect: get user via Extension
async fn dashboard_with_user(Extension(user): Extension<AuthUser>) -> String {
    format!("Welcome, {}!", user.label)
}

Middleware Comparison

MiddlewareUnauthenticatedAvailable in Handler
is_authenticated_redirectRedirect to loginCsrfToken
is_authenticated_user_redirectRedirect to loginAuthUser (includes csrf_token)
is_authenticated_401Return 401CsrfToken
is_authenticated_user_401Return 401AuthUser (includes csrf_token)

Note: The _user_ variants perform an additional database query to fetch user information. Use the non-user variants when you only need to verify authentication without accessing user details in your handler.

Note: Handlers receive the CSRF token to embed in rendered pages. See CSRF Token Handling for details.

When to Use Each Method

MethodUse Case
AuthUser extractorSimple protected routes where you need user info
Option<AuthUser>Pages that work for both authenticated and anonymous users
is_authenticated[_user]_redirectProtect routes with redirect to login for unauthenticated users
is_authenticated[_user]_401API endpoints that should return 401 instead of redirect