Better Auth in Rust

Security

Security features, best practices, and threat mitigations in Better Auth.

Better Auth is designed with security as a core principle. This page documents the security mechanisms built into the framework and best practices for production deployments.

Password Security

Hashing Algorithm

Better Auth uses Argon2id for password hashing, which is the current recommendation from OWASP and the winner of the Password Hashing Competition. Argon2 is resistant to:

  • GPU-based attacks
  • Side-channel attacks
  • Time-memory trade-off attacks

Passwords are hashed with a unique random salt per user.

Password Policies

Configure minimum password requirements:

let config = AuthConfig::new("secret-key-at-least-32-chars")
    .password_min_length(8);

Session Security

Token Generation

Session tokens are generated using cryptographically secure random number generators (OsRng). Tokens are sufficiently long to prevent brute-force guessing.

use better_auth::{AuthConfig, SameSite};

let mut config = AuthConfig::new("secret-key");
config.session.cookie_secure = true;        // HTTPS only
config.session.cookie_http_only = true;     // Not accessible via JavaScript
config.session.cookie_same_site = SameSite::Lax; // CSRF protection
SettingRecommendedDescription
cookie_securetrueOnly send over HTTPS
cookie_http_onlytrueBlock JavaScript access (prevents XSS token theft)
cookie_same_siteLaxPrevents CSRF while allowing top-level navigations

See Cookies for more details.

Session Expiration

Sessions have a configurable expiration time. Expired sessions are automatically rejected.

use std::time::Duration;

let config = AuthConfig::new("secret-key")
    .session_expires_in(Duration::from_secs(7 * 24 * 3600)); // 7 days

CSRF Protection

Better Auth includes built-in CSRF protection that validates the Origin or Referer header on mutating requests (POST, PUT, DELETE, PATCH).

use better_auth::{AuthBuilder, AuthConfig, CsrfConfig};

let config = AuthConfig::new(secret)
        .trusted_origins(vec!["https://myapp.com".to_string()]);

let auth = AuthBuilder::new(config)
    .csrf(CsrfConfig::new().enabled(true))
    .build()
    .await?;

Requests from untrusted origins receive a 403 Forbidden response.

CORS

Properly configured CORS prevents unauthorized cross-origin access:

use better_auth::CorsConfig;

let auth = BetterAuth::new(config)
    .cors(
        CorsConfig::new()
            .allowed_origin("https://myapp.com")
            .allow_credentials(true)
    )
    .build()
    .await?;

Rate Limiting

Rate limiting protects against brute-force attacks. See Rate Limiting for detailed configuration.

Recommended limits for authentication endpoints:

EndpointLimitWindow
/sign-in/email1060s
/sign-up/email560s
/forget-password3300s
/two-factor/verify-*560s

OAuth Security

PKCE

All OAuth flows use PKCE (Proof Key for Code Exchange) with S256 challenge method. This prevents authorization code interception attacks.

State Parameter

OAuth state is validated on every callback to prevent CSRF attacks. State tokens expire after 10 minutes.

Token Storage

OAuth access tokens and refresh tokens are stored server-side in the database. They are never exposed directly to the client.

Two-Factor Authentication

TOTP

TOTP secrets are stored securely in the database. Codes are time-based with a configurable period (default: 30 seconds).

Backup Codes

Backup codes are hashed with Argon2 before storage. Only the plaintext codes are returned once during generation.

OTP

Email-based OTP codes expire after 5 minutes and are single-use.

Body Size Limits

Reject oversized request bodies to prevent denial-of-service:

use better_auth::BodyLimitConfig;

let auth = BetterAuth::new(config)
    .body_limit(BodyLimitConfig::new().max_bytes(512 * 1024))
    .build()
    .await?;

Secret Key

The secret key is used for signing and verification. It must be:

  • At least 32 characters long
  • Kept secret and never committed to version control
  • Loaded from environment variables in production
let secret = std::env::var("AUTH_SECRET").expect("AUTH_SECRET must be set");
let config = AuthConfig::new(&secret);

Production Checklist

  • Set cookie_secure: true (requires HTTPS)
  • Set cookie_http_only: true
  • Set cookie_same_site: Lax or Strict
  • Enable CSRF protection with trusted origins
  • Configure rate limiting on all authentication endpoints
  • Use a strong, random secret key (32+ characters)
  • Load secrets from environment variables, not code
  • Set appropriate session expiration times
  • Configure CORS with specific allowed origins
  • Enable body size limits
  • Use HTTPS in production
  • Set up proper X-Forwarded-For headers in your reverse proxy
  • Enable email verification for sign-ups
  • Consider enabling 2FA for admin users

See Also

On this page