Login protection
What this page covers
Section titled “What this page covers”The login page is the only entrance from outside, and it gets used to guess passwords and to probe which accounts exist. This page covers three things: how guessing is held off, why the response does not say what was wrong, and where a user’s signed-in state is kept.
Two defenses that complement each other
Section titled “Two defenses that complement each other”Source rate limiting
Section titled “Source rate limiting”The login endpoint is rate limited per source address, which stops password spraying that rotates through accounts.
Going over returns 429, and the response reveals neither the remaining allowance nor the reset time.
The source address comes from the system’s trusted proxy setting rather than from trusting request headers; when deployed behind a reverse proxy, fill in TRUSTED_PROXIES in .env.
The single sign-on endpoints carry their own per-source rate and overall capacity ceiling.
Account lockout
Section titled “Account lockout”The security policy decides the lockout threshold and duration, shipped as 10 consecutive failures and 30 minutes. Password failures and two-factor failures share one counter, and a successful login resets it to zero. Directory accounts fall under the same counter. During a lockout the response says only that the account is temporarily locked, without saying for how long or how many attempts were made. An administrator can unlock the account by hand on the Users page.
Why the responses look so alike
Section titled “Why the responses look so alike”An account that does not exist, a wrong password, and a disabled account with a wrong password all return the same status code and the same error code, and the account state is evaluated after the credential is verified. This is deliberate: someone without a credential should not be able to use the login page as a roster of account names.
A correct credential on a disabled account is the exception. That case returns an error code of its own, because the user needs to know to go and find an administrator.
Where the credentials live
Section titled “Where the credentials live”The access credential is valid for a fixed 15 minutes and lives only in the memory of the tab, written to neither localStorage, sessionStorage, IndexedDB, nor a cookie. The renewal credential travels only in an HttpOnly cookie, with a SameSite restriction and a path narrowed to the authentication endpoints.
When a page loads, the navigation guard exchanges that cookie for an access credential before it lets a protected page through. Tabs pass events between themselves rather than the credential itself, so signing out in one tab clears every other tab in the same browser immediately.
Whether the cookie is sent only over encrypted connections is controlled by the security policy “keep the signed-in state only over https”, which ships on; see TLS and certificates for how to set it.
Session governance
Section titled “Session governance”A renewal has to satisfy four conditions at once: the credential has not been revoked, the absolute lifetime has not been exceeded (12 hours as shipped), the time since the last activity is within the idle ceiling (60 minutes as shipped), and the source address falls inside the account’s allowed list.
Every successful renewal rotates the credential. When an old credential is replayed, every renewal credential of that account is revoked. Signing out revokes the current credential; a password change, a deactivation, or a lockout revokes all of them, and a deactivation also cuts the connections in progress. A role change advances the credential generation, which invalidates the older ones.
The access credential is accepted only from the Authorization header, never from the address.
What auditors can see
Section titled “What auditors can see”- A successful login and a failed one each leave a record with the account, source address, and result.
- Rate limiting is recorded as an aggregated row, with the reason, source address, count, and start and end times.
- Credential renewal leaves a trail in three states: a normal rotation, a refusal, and a detected replay.
- A refused source address leaves a record with the address and the basis for the decision, and it does not count toward failed logins.
- The first login of an account from a given source address leaves an additional observation record.