Better Auth in Rust

Quick Start

Generate an auth schema and run an Axum server.

This example starts an Axum server with an in-memory SQLite database. Data resets when the process stops.

Dependencies

Cargo.toml
[dependencies]
better-auth = { version = "1.0.0-alpha.3", git = "https://github.com/better-auth-rs/better-auth-rs", branch = "master", features = ["axum", "seaorm2"] }
axum = "0.8"
tokio = { version = "1", features = ["macros", "rt-multi-thread", "net"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"

Generate the schema

cargo install better-auth-cli --git https://github.com/better-auth-rs/better-auth-rs --branch master --locked
better-auth-rs generate --output src/auth_schema.rs

The generated file contains all four core entities, AppAuthSchema, and create_auth_tables. Add --plugins two-factor,api-key when your app registers those plugins. Existing output files are preserved unless you pass --force.

Start the server

Save this complete application as src/main.rs:

src/main.rs
mod auth_schema;

use auth_schema::{AppAuthSchema, create_auth_tables};
use better_auth::{AuthConfig, BetterAuth};
use better_auth::integrations::axum::AxumIntegration;
use better_auth::plugins::{EmailPasswordPlugin, SessionManagementPlugin};
use better_auth::seaorm::{Database, SeaOrmStore};
use std::sync::Arc;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let database = Database::connect("sqlite::memory:").await?;
    create_auth_tables(&database).await?;
    let config = AuthConfig::new(std::env::var("AUTH_SECRET")?)
        .base_url("http://localhost:3000");
    let store = SeaOrmStore::<AppAuthSchema>::new(config.clone(), database);
    let auth = Arc::new(
        BetterAuth::<AppAuthSchema>::new(config)
            .store(store)
            .plugin(EmailPasswordPlugin::new().enable_signup(true))
            .plugin(SessionManagementPlugin::new())
            .build()
            .await?,
    );
    let app = axum::Router::new()
        .nest("/auth", auth.clone().axum_router())
        .with_state(auth);
    let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await?;
    axum::serve(listener, app).await?;
    Ok(())
}

Set AUTH_SECRET to a random secret of at least 32 characters. Run cargo run.

For a persistent database, run create_auth_tables once from your initial migration. The function fails if tables already exist. Use versioned application migrations for subsequent changes.

Register and sign in

COOKIE_JAR=$(mktemp)

curl --fail-with-body http://localhost:3000/auth/sign-up/email \
  -H 'Content-Type: application/json' \
  -d '{"email":"[email protected]","password":"example-password-123","name":"Example"}'

curl --fail-with-body http://localhost:3000/auth/sign-in/email \
  -c "$COOKIE_JAR" \
  -H 'Content-Type: application/json' \
  -d '{"email":"[email protected]","password":"example-password-123"}'

Sign-in sets a signed session cookie. Save the cookie in a cookie jar and send the cookie with subsequent requests:

curl --fail-with-body http://localhost:3000/auth/get-session \
  -b "$COOKIE_JAR"

rm "$COOKIE_JAR"

The default configuration uses cookies. To accept Bearer credentials, explicitly enable config.session.bearer as described in the session guide.

For business routes, use CurrentSession<AppAuthSchema> as shown in the Axum guide. The independent consumer tests this public schema generation flow with registration, login, protected routes, API keys, and TOTP.

Run devenv shell -- bun scripts/quick-start-check.ts to compile this document's application and execute its registration, sign-in, and session commands.

On this page