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_sessionswith a 7-day expiry enforced on every request. - Session cookies are
HttpOnly; SameSite=Strict.Secureis 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§
- Csrf
Token - Newtype wrapper stored in the per-request
Contextby 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. - Login
Rate Limiter - 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
Profilestruct, 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
/adminwhen 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
authenticatemiddleware 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_usersandrustio_sessionsif they don’t already exist. Called frommigrations::applybefore any user-level migration runs, so auth tables are always present in a project’s DB. - identity
- in_
production truewhenRUSTIO_ENVindicates a production deployment.- normalise_
email - Normalise an email for storage + comparison.
- require_
admin - require_
auth - resolve_
identity - Resolve a session token into an
Identity, orNoneif the token is missing, unknown, expired, or points at an inactive / deleted user. - resolve_
identity_ with_ session - Resolve a session token into an
Identityplus the liveSessionit came from, orNoneif 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.