Skip to main content

Module auth

Module auth 

Source
Expand description

Authentication: users, passwords, sessions, middleware.

This is the identity layer every RustIO system depends on. It is infrastructure, not an application feature — the User struct deliberately stays minimal (id, email, password_hash, is_active, role) so projects can extend it with a separate Profile model rather than widening this one.

§Security model

  • Passwords are stored as argon2id PHC strings (see password).
  • Sessions are 32-byte OS-random tokens, stored in rustio_sessions with a 7-day expiry enforced on every request.
  • Session cookies are HttpOnly; SameSite=Strict. Secure is documented at the deployment boundary (terminator / proxy) because the server can’t reliably detect HTTPS on its own.
  • Failed logins return a generic error (invalid email + wrong password are indistinguishable from outside) to prevent user enumeration. An inactive account is called out explicitly — it’s already an administrative decision, not a secret.

§Wiring

Generated projects register the middleware via router.wrap(auth::authenticate(db.clone())). There are no global singletons; every project owns its Db handle and passes it in.

Modules§

csrf
Per-session CSRF tokens.
password
Password hashing with argon2id.
session
Database-backed sessions keyed by a 256-bit OS-random token.
user
User queries. Validates email format on create + on lookup (inputs are normalised to trimmed-lowercase before the DB sees them).

Structs§

CsrfToken
Newtype wrapper stored in the per-request Context by the authenticate middleware after a successful session lookup. Admin form renderers read it to inject <input name="_csrf">; admin POST handlers compare it (constant-time) against the submitted value before mutating anything.
Identity
Per-request identity snapshot, attached by the auth middleware and read via identity / require_auth / require_admin.
LoginRateLimiter
In-memory counter of recent failed login attempts, keyed by email.
Session
Session record.
User
User record. Infrastructure, not an application model — extend user data via a separate Profile struct, not by widening this one.

Constants§

ROLE_ADMIN
Role string meaning “has admin access”. Anything else is treated as a regular user; the admin middleware only unlocks /admin when this matches exactly.
ROLE_USER
Default role for newly-created users.
SESSION_COOKIE
Name of the cookie that carries the session token from the browser to the server. Used by both the login handler (to set) and the authenticate middleware (to read).
SESSION_TTL_DAYS
How long a newly-created session is valid before the middleware treats it as expired. Not configurable in 0.4.0 — kept fixed so the security surface stays small.

Functions§

authenticate
Build the authenticate middleware for this project.
bearer_token
Read the Authorization: Bearer <token> header if present. Kept as a primitive so projects can layer their own Bearer/API-token auth on top of session auth.
dummy_password_hash
A precomputed argon2id hash used by the login handler to equalise the wall-clock cost of “user doesn’t exist” and “user exists, wrong password”. Without this, an attacker can enumerate valid emails by measuring response time (the verify branch costs ~50 ms; the skip-verify branch is a few ms).
ensure_core_tables
Create rustio_users and rustio_sessions if they don’t already exist. Called from migrations::apply before any user-level migration runs, so auth tables are always present in a project’s DB.
identity
in_production
true when RUSTIO_ENV indicates a production deployment.
normalise_email
Normalise an email for storage + comparison.
require_admin
require_auth
resolve_identity
Resolve a session token into an Identity, or None if the token is missing, unknown, expired, or points at an inactive / deleted user.
resolve_identity_with_session
Resolve a session token into an Identity plus the live Session it came from, or None if the token is missing, unknown, expired, or points at an inactive / deleted user.
validate_email
Validate an email for admin-level correctness. Intentionally not full RFC 5322 — we accept any address with a local part, an @, and a domain containing a .. That rejects the common-mistake class (typos, obvious malformed input) without pretending to police delivery validity.