rustauth-axum 0.2.0

Axum integration for RustAuth.
Documentation

rustauth-axum

Axum adapter for RustAuth.

What It Is

rustauth-axum mounts the framework-neutral RustAuth handler into an Axum application. Use it when your server is built with Axum and you want RustAuth routes under a path such as /api/auth.

What It Provides

  • RustAuthAxumExtmount_routes and mount_at_base_path for mounting on both RustAuth and Arc<RustAuth>. Both take RustAuthAxumOptions explicitly (use RustAuthAxumOptions::default() for defaults).
  • handle — escape hatch for single-request integration without building a router; takes RustAuthAxumOptions as the second argument.
  • RustAuthAxumOptions — request body limits, ConnectInfo propagation, and optional base URL inference behind explicit proxy trust.
  • Request and response conversion that preserves headers, extensions, and HTTP metadata.

Quick Start

use rustauth::prelude::*;
use rustauth_axum::RustAuthAxumExt;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let auth = RustAuth::builder()
        .secret("secret-a-at-least-32-chars-long!!")
        .base_url("https://app.example.com/api/auth")
        .build()
        .await?;

    // Apply schema with `rustauth db migrate` before serving traffic.

    let app = auth.mount_at_base_path(
        RustAuthAxumOptions::new().body_limit(1024 * 1024),
    )?;
    # let _ = app;
    Ok(())
}

When Axum is exposed directly to clients, run it with connection info so RustAuth can use the real peer socket IP for rate limiting:

use std::net::SocketAddr;
use axum::serve;
use tokio::net::TcpListener;

# async fn run(app: axum::Router) -> Result<(), Box<dyn std::error::Error>> {
let listener = TcpListener::bind("127.0.0.1:3000").await?;
serve(listener, app.into_make_service_with_connect_info::<SocketAddr>()).await?;
# Ok(())
# }

Choosing a mount API

Goal API Notes
Standalone auth server auth.mount_at_base_path(RustAuthAxumOptions::default()) Nests at base_path
Shared Arc<RustAuth> in app state auth.mount_routes(RustAuthAxumOptions::default()) + your .nest() You control prefix
Let rustauth-axum nest auth.mount_at_base_path(RustAuthAxumOptions::default()) Do not nest again
Custom state on auth routes mount_routes(options) Same as above

When you nest manually with mount_routes(), ensure your outer .nest(prefix, …) uses the same path as RustAuthOptions::base_path and that base_url matches. The fallible mount helpers validate this; you can also call validate_mount_config explicitly before nesting.

Notes

  • Default mount path comes from RustAuthOptions::base_path, falling back to /api/auth.
  • base_path("/") and base_path("") mount RustAuth routes at the application root.
  • Request bodies are collected before core and capped at 10 MiB by default.
  • Configure RustAuthOptions::base_url for production deployments. If you intentionally need request-derived public URLs, enable RustAuthAxumOptions::infer_base_url_from_request(true) and configure trusted origins explicitly.
  • Public x-forwarded-host and x-forwarded-proto headers are ignored unless both base URL inference and RustAuthAxumOptions::trust_proxy_headers_for_base_url(true) are enabled.
  • Do not trust public x-forwarded-for headers unless traffic is terminated by a trusted reverse proxy.
  • Do not run Tower/Axum body-consuming middleware on auth routes before rustauth-axum (same idea as avoiding express.json() before Better Auth on Express).

Status

Experimental beta. Router composition, request extraction, and adapter options may change before stable release.

Better Auth compatibility

Server-side Axum HTTP adapter for mounting RustAuth routes. Aligned with Better Auth 1.6.9 where it matters for this crate; RustAuth is not a line-by-line port.

For route-level parity, test counts, intentional differences, and known gaps, see UPSTREAM.md.

Links