rs-auth 0.1.0

Composable authentication for Rust with Axum and Postgres.
Documentation

rs-auth

Composable authentication for Rust, inspired by Better Auth. The rs-auth facade crate re-exports rs-auth-core, rs-auth-postgres, and rs-auth-axum for convenient access to the authentication stack.

Current Status

Phase 1 (Email/Password Authentication) is complete and production-ready.

Phase 2 (OAuth) exists with support for Google and GitHub providers, but is still early and experimental. The API may change in future releases.

Features

  • Email/password signup and login
  • Argon2id password hashing
  • Database-backed sessions with opaque tokens (SHA-256 hashed)
  • Email verification
  • Password reset
  • Signed cookies (via axum-extra)
  • Configurable session and token TTLs
  • Auto sign-in after signup
  • CLI for migrations and cleanup
  • OAuth login and callback for Google and GitHub (experimental)

Workspace Layout

rs-auth/
├── auth/          -> rs-auth (facade crate)
├── core/          -> rs-auth-core (domain logic)
├── pg/            -> rs-auth-postgres (PostgreSQL store)
├── axum/          -> rs-auth-axum (Axum handlers & router)
├── cli/           -> rs-auth-cli (CLI tool)
└── examples/
    └── basic/     -> minimal example app

Quick Start

Add rs-auth to your Cargo.toml:

[dependencies]
rs-auth = "0.1"
sqlx = { version = "0.8", features = ["runtime-tokio", "postgres"] }
axum = "0.8"
axum-extra = { version = "0.10", features = ["cookie-signed"] }
tokio = { version = "1", features = ["full"] }
tracing-subscriber = "0.3"

Create a minimal application:

use axum_extra::extract::SignedCookieJar;
use axum::{Json, Router, extract::State, routing::get};
use rs_auth_axum::{AuthState, auth_router, extract::resolve_optional_user};
use rs_auth_core::{AuthConfig, AuthService, email::LogEmailSender};
use rs_auth_postgres::{AuthDb, run_migrations};
use serde_json::json;

#[tokio::main]
async fn main() {
    tracing_subscriber::fmt::init();

    let database_url = std::env::var("DATABASE_URL")
        .unwrap_or_else(|_| "postgres://postgres:postgres@127.0.0.1:5432/postgres".to_string());

    let pool = sqlx::PgPool::connect(&database_url).await.unwrap();
    run_migrations(&pool).await.unwrap();

    let db = AuthDb::new(pool);
    let auth_service = AuthService::new(
        AuthConfig::default(),
        db.clone(),
        db.clone(),
        db.clone(),
        db,
        LogEmailSender,
    );
    let auth_state = AuthState::new(auth_service);

    let app = Router::new()
        .route("/", get(|| async { "rs-auth basic example" }))
        .route("/me", get(me))
        .nest("/auth", auth_router(auth_state.clone()))
        .with_state(auth_state);

    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    tracing::info!("listening on {}", listener.local_addr().unwrap());
    axum::serve(listener, app).await.unwrap();
}

async fn me(
    State(state): State<AuthState<AuthDb, AuthDb, AuthDb, AuthDb, LogEmailSender>>,
    jar: SignedCookieJar,
) -> Json<serde_json::Value> {
    let user = resolve_optional_user(&state, &jar).await.unwrap();
    Json(json!({ "user": user.user }))
}

Configuration

The AuthConfig struct controls authentication behavior:

pub struct AuthConfig {
    pub secret: String,                    // Cookie signing secret
    pub session_ttl: Duration,             // Default: 30 days
    pub verification_ttl: Duration,        // Default: 1 hour
    pub reset_ttl: Duration,               // Default: 1 hour
    pub token_length: usize,               // Default: 32 bytes
    pub email: EmailConfig,
    pub cookie: CookieConfig,
    pub oauth: OAuthConfig,
}

EmailConfig

pub struct EmailConfig {
    pub send_verification_on_signup: bool,        // Default: true
    pub require_verification_to_login: bool,      // Default: false
    pub auto_sign_in_after_signup: bool,          // Default: true
    pub auto_sign_in_after_verification: bool,    // Default: false
}

CookieConfig

pub struct CookieConfig {
    pub name: String,              // Default: "rs_auth_session"
    pub http_only: bool,           // Default: true
    pub secure: bool,              // Default: true
    pub same_site: SameSite,       // Default: Lax
    pub path: String,              // Default: "/"
    pub domain: Option<String>,    // Default: None
}

CLI

The rs-auth-cli binary provides three commands:

Run Migrations

rs-auth-cli migrate --database-url postgres://user:pass@localhost/db

Creates the necessary database tables for users, sessions, verification tokens, and OAuth accounts.

Generate Migration

rs-auth-cli generate <name>

Generates a new migration file template.

Cleanup Expired Tokens

rs-auth-cli cleanup --database-url postgres://user:pass@localhost/db

Removes expired sessions and verification tokens from the database.

OAuth (Experimental)

Google and GitHub OAuth providers are supported. Configure OAuth with OAuthConfig:

use rs_auth_core::{AuthConfig, OAuthConfig, OAuthProviderEntry};

let mut config = AuthConfig::default();
config.oauth = OAuthConfig {
    providers: vec![
        OAuthProviderEntry {
            provider_id: "google".to_string(),
            client_id: "your-google-client-id".to_string(),
            client_secret: "your-google-client-secret".to_string(),
            redirect_url: "http://localhost:3000/auth/callback/google".to_string(),
            auth_url: None,
            token_url: None,
            userinfo_url: None,
        },
        OAuthProviderEntry {
            provider_id: "github".to_string(),
            client_id: "your-github-client-id".to_string(),
            client_secret: "your-github-client-secret".to_string(),
            redirect_url: "http://localhost:3000/auth/callback/github".to_string(),
            auth_url: None,
            token_url: None,
            userinfo_url: None,
        },
    ],
    allow_implicit_account_linking: true,
    success_redirect: Some("/dashboard".to_string()),
    error_redirect: Some("/login?error=oauth".to_string()),
};

OAuth transient state (CSRF tokens and PKCE verifiers) is currently stored in the verifications table to reuse existing infrastructure. This approach may change in future versions if OAuth features expand significantly.

Note: The OAuth implementation is early and the API may change in future releases.

API Endpoints

The auth_router provides the following endpoints:

Method Path Description
POST /auth/signup Create a new user account
POST /auth/login Log in with email and password
POST /auth/logout Log out and invalidate session
GET /auth/session Get current session information
GET /auth/sessions List all sessions for current user
GET /auth/verify/{token} Verify email with token
POST /auth/forgot Request password reset
POST /auth/reset Reset password with token
GET /auth/login/{provider} Initiate OAuth login (experimental)
GET /auth/callback/{provider} OAuth callback handler (experimental)

License

Licensed under either of:

  • MIT License
  • Apache License, Version 2.0

at your option.