Account security
The /admin dashboard and the OAuth consent login are protected by several
independent layers. Regular MCP tokens (/mcp) are unaffected — this page is
about human login.
Two-factor authentication (TOTP) — optional
Section titled “Two-factor authentication (TOTP) — optional”Each user account may turn on time-based one-time-password 2FA (RFC 6238, compatible with Google Authenticator, Authy, 1Password, etc.). It is optional and per account — 2FA is not forced on anyone.
- Turning it on. A user opens
/admin/security(or goes straight to/admin/setup-2fa), scans the QR (or types the manual key), and confirms a 6-digit code. From then on that account uses 2FA. - Login is two-step once enabled. For an enrolled account, password comes
first, then the 6-digit code at
/admin/2fa; a correct password alone only yields a short-lived partial session that can reach nothing but the code page. An account without 2FA logs straight in. - Turning it off. The user goes to
/admin/securityand enters a current 6-digit code (or a backup code) to disable it — proof of possession, so a stolen session cannot silently strip the factor. (API:POST /admin/api/2fa/disable.) - Backup codes. Ten one-time codes are shown once at enrolment. Each works a single time in place of the authenticator if you lose your phone. Save them somewhere safe — they are not shown again.
- The secret is encrypted at rest (AES-GCM with
MASTER_TOKEN).
Recovery (lost authenticator)
Section titled “Recovery (lost authenticator)”A super-admin opens /admin/users, finds the row, and clicks Reset 2FA.
That removes the authenticator and revokes the user’s sessions; the user can
re-enrol later. (API: POST /admin/api/users/:id/reset-2fa.)
Brute-force lockout
Section titled “Brute-force lockout”Login attempts are rate-limited on two independent counters over a rolling 15-minute window:
- 5 failures for one username → that account is locked out.
- 20 failures from one IP → that IP is locked out.
A locked login is refused with HTTP 429 and a Retry-After header, before
the password is even checked (so it also protects CPU). A successful login
clears the username’s failure streak. Applies to both /admin/login and the
OAuth consent login.
Password hashing
Section titled “Password hashing”Passwords are PBKDF2-HMAC-SHA256, 600,000 iterations (OWASP 2023), with a random 16-byte salt. Hashes created at an older/lower cost are transparently re-hashed on the next successful login — no mass reset, no lockout.
Password policy (user-chosen passwords): ≥ 8 characters, ≥ 1 digit, ≥ 1 special character. Admin-generated provisioning passwords are random and high-entropy by construction.
CSRF protection
Section titled “CSRF protection”Every state-changing /admin/* request (login, JSON APIs, actions) must be
same-origin: a cross-site request carrying an attacker Origin/Referer is
refused with HTTP 403. The session cookie is HttpOnly, Secure,
SameSite=Strict, scoped to /admin.
Session revocation
Section titled “Session revocation”Each session cookie carries a session epoch. Advancing a user’s stored epoch invalidates every existing cookie for that user at once. The epoch is bumped automatically on:
- password change (self-service or an admin reset) — other sessions drop; the session that made the change is re-issued so it stays signed in;
- 2FA reset;
- Log out everywhere —
POST /admin/api/users/:id/logout-all(super-admin; also a button on the/admin/usersrow).
A revoked cookie is rejected with E_SESSION_REVOKED (401) / a redirect to
login.
Idle timeout
Section titled “Idle timeout”Sessions carry an issued-at timestamp and expire after 2 hours of inactivity, independent of the absolute cookie lifetime. Activity slide-refreshes the cookie (past the half-way mark), so an active user is never logged out mid-work; only an abandoned session ages out.
Individual super-admins
Section titled “Individual super-admins”Instead of sharing the master token, grant a user account the Super-admin
flag (/admin/users → create/edit → Super-admin, or is_super via the API).
Such a user logs in with their own password (and 2FA if they turned it on) and
gets full /admin privileges — with their own name in the audit log. The master
token is kept as break-glass alongside them.
At a glance
Section titled “At a glance”| Layer | Control | Refusal |
|---|---|---|
| Password | PBKDF2 600k + rehash-on-login | — |
| Brute force | 5/username, 20/IP per 15 min | 429 + Retry-After |
| CSRF | same-origin required | 403 |
| 2FA | optional TOTP + backup codes (per account) | challenge when enabled |
| Session | epoch revocation | E_SESSION_REVOKED (401) |
| Idle | 2 h sliding | redirect to login |
| Identity | individual super-admins (is_super) | — |