Architecture
One binary, two listeners, one store
The server is one Rust binary. It serves the issuers, the sign-in pages and the account portal on its public listener and the admin console on a listener of its own, and it keeps nothing that matters anywhere but in TessariDB.
Listeners
The public side and the console apart
Either listener speaks TLS when given a certificate, or plain HTTP for a TLS terminator in front of it. The console signs operators in through the company's own issuer, as a client of it.
| Listener | Default | Serves |
|---|---|---|
| public | 0.0.0.0:8443 | discovery, key sets, authorization, token, revocation, UserInfo, logout, the sign-in and consent pages, the account portal |
| admin | 0.0.0.0:9443 | the admin console at <ADMIN_URL>/admin/<company>/, at an origin other than the public one |
The store
Every piece of state is a TessariDB record
A region's companies live in the namespace oauth_<REGION>, one database per company, beside the region's directory. Codes, grants, consents, refresh families, sessions, sealed signing keys, replay and throttle counters and the audit trail are that database's records, transactions and spaces.
A code is stored by its SHA-256 digest only and spent with one conditional write, which is what makes one redemption win among any number of simultaneous ones.
- At start the server records each configured company in the directory, provisions its database and makes it active.
- It makes a signing key for any company without one, then checks every five minutes for keys due to rotate.
- The audit trail is a TessariDB topic kept for 400 days.
Keys
Two files the store never sees
Signing keys and other secrets are sealed under a 32-byte key-encryption key; passwords are hashed with Argon2id under a pepper. Both are files on the server, readable by its own user only, so neither is in a backup of the store. reseal moves every sealed secret to a new key-encryption key.
Regions
One home region per company
Each region is its own TessariDB deployment. A server given its peers reads every other region's directory every five seconds and answers a request for a company another region serves with 307 Temporary Redirect to the same path there, so identity data is never proxied across regions.
- region us
GET id.us.example.com/acme/oauth2/authorize?…307 → eu - region eu
GET id.eu.example.com/acme/oauth2/authorize?…answered where acme lives
Sender-constrained tokens
A token that only its key can use
With DPoP (RFC 9449) the client proves possession of a key on every request, and the token carries that key's thumbprint (RFC 7638). A proof is spent once, and a bound token presented as a bearer token is refused.
- client, with its key
Authorization: DPoP eyJ… + DPoP: proof(ath, htm, htu)answered - someone who copied the token
Authorization: DPoP eyJ… + no proof from the key401 invalid_token - the same token as a bearer token
Authorization: Bearer eyJ…refused