How somebody signs in, where they are asked to prove themselves again, and what holds a session together.
Two ways in, kept apart
The people who use the service sign in with Google or Apple, on a screen with no password field at all. The people who operate it sign in on a separate screen, with a password and an authenticator code. Keeping them apart means the surface an attacker can throw passwords at is one screen that customers never open.
Who
Where
How
Second factor
People who use the service
/login
Google or Apple
Handled by Google or Apple
People who operate it
/login/staff
Address and password
An authenticator app. Required.
An account is tied to the provider it was created with, and no second way in is ever grown on it. A Google account cannot acquire a password later — there is no screen for it and the server refuses the call. One account, one way in.
An operator without an authenticator app can do exactly two things: set one up, and sign out. Everything else is refused until they have. This is enforced in front of every endpoint rather than inside each one, so an endpoint added tomorrow is covered without anybody remembering to cover it.
Where a code is asked for
Signing in says who somebody is. It does not say that the person at the keyboard right now is still them, and it says nothing about intent. So for the screens that reach personal data — other people's names, addresses and phone numbers — and for the operations that cannot be undone, the account is asked for its code once more.
The code is asked for at the door, not at sign-in: a session that never opens these screens is never asked. Sessions last fourteen days and do not extend, so in practice this is once per sign-in — except for the operations in the bottom band, which ask again immediately beforehand because the question there is not whether it is still you, but whether you meant to do it now.
Nothing is hidden from view because of this. What is behind the line is refused, with a message saying what to do; the screen itself opens and asks for the code. Somebody who has not set up an authenticator app is told to set one up, rather than being shown a box for a code they cannot produce.
If a code was already entered to sign in — which is how the operator accounts get in, and how anybody finishing two-factor setup got there — the session starts having answered this, and is not asked again. The question at the door and the question at that sign-in are the same question; asking it twice in a minute reads as the setup not having worked.
If you are building against the API, the refusal arrives as 412 with an X-Step-Up-Reason header. The three values and what each one asks of the person are in Chapter 6.
How long things last
What
Lifetime
What it is for
access token
15 minutes
Authorises an API call. Short on purpose.
refresh token
14 days
Renews the access token. Single use: if one is presented twice, every token in that session is revoked.
Session
14 days, and it does not extend
After that, signing in again — which is also when a code is asked for again.
Step-up, for personal data
The session
In practice, once per sign-in.
Step-up, for irreversible operations
15 minutes
Asked for immediately before the operation.
Email confirmation link
24 hours
Confirms an address at registration.
Password reset link
1 hour
Only exists for accounts that have a password.
Email change link
1 hour
Sent to the new address.
Invitation link
7 days
Counts against the tenant's member limit while outstanding.
API key
30 days by default, 90 at most
See below.
Passwords
Length: 8 to 128 characters.
Storage: PBKDF2-HMAC-SHA256, 100,000 iterations. That number is the ceiling the Cloudflare Workers runtime accepts, not a choice — it is below the 600,000 usually recommended, and the gap is compensated for by limiting sign-in attempts and by requiring a second factor.
A tenant may ask for more: An owner can require a longer minimum, or upper case, digits and symbols — but only in the stricter direction. Somebody who belongs to several tenants is held to the strictest of them.
Who has one: Only accounts created with one. For a Google or Apple account, changing or resetting a password is refused rather than quietly creating one.
What a session rests on
A signature alone is not enough to let a request through. Every request also reads the session and the account out of the database, which is what makes revoking anything take effect at once instead of at the end of the token's fifteen minutes.
1. Where the credential comes from
From the browser, a cookie the page's own JavaScript cannot read. From a native app, an Authorization: Bearer header. The cookie is preferred when both are present.
2. The signature is checked at the edge
The token carries a user id and a session id, signed with HMAC-SHA256. Tampering is detected without a round trip to any database.
3. Then the state is read anyway
Signing out, revoking a session, disabling an account, resetting a password and changing a role all take effect from the next request. Permissions come from the database rather than from a claim in the token, so lowering somebody's role bites immediately. Skipping this check would make every revocation up to fifteen minutes late.
What this covers
Script injected into a page cannot read the token: the cookie is HttpOnly.
A request from another site cannot use it: the cookie is SameSite=Lax with a __Host- prefix, and a request that authenticates by cookie must also come from a known origin. Two mechanisms, so that neither is load-bearing alone.
Nothing travels in the clear: every cookie is Secure, so it is only ever sent over HTTPS.
API keys
A key is a bearer secret that travels on every request, so the window between a leak and its expiry is the whole exposure. Three things keep that window small.
It expires: 30 days unless a longer one is asked for, and 90 at the outside. The management screen starts warning 14 days out.
It can only read, unless you said otherwise: New keys are read-only unless read-write was chosen at issue. What a read-only key may call is an allowlist, so an endpoint added tomorrow is closed to it until somebody decides it is safe.
It can be rotated without downtime: Every tenant can hold at least two keys at once, on every plan. With a single slot, replacing a key would mean an outage — and a tenant that cannot rotate without an outage does not rotate, which turns the expiry date from a safeguard into a scheduled failure.
A key cannot do anything that needs a code, because there is nobody to ask. In practice this means a key verifies tokens and reads which tenants an account belongs to; it does not administer anything. Issuing and revoking keys is done by a person, signed in, who has just entered a code.
Rate limits
Sign-in, registration, password reset, code attempts and everything else are limited per account, per session, per key and per source address. The full table is kept in one place so the two chapters cannot drift apart: Chapter 6, Rate limits.
Counting is done by a single-threaded object that is authoritative for its key, rather than by an eventually consistent store. A limit of 30 that lets 60 through is not a limit.