Skip to content

The three-key model

View as Markdown

The three-key model is the heart of Sudomimus’s session security for the browser-mediated flow. Every authentication round-trip through Connect mints three distinct keys at different moments, held by different parties, defending against different classes of attack. A successful /redeem must present all three.

This is not about the long-lived material Sudomimus uses to sign tokens (covered in Tokens and verification). It is about how a single browser-mediated login proves itself.

KeyCreated byHeld byVisible to
exposureKey/establishApplication backend, then the browserBrowser, URL, via.sudomimus.com
hiddenKey/establishApplication backend onlyBackend
confirmationKeyvia.sudomimus.com, after the user authenticatesApplication backend (via callback)Browser, URL

exposureKey and hiddenKey are issued together as a pair by the same /establish call. They belong to one session, and only that session.

Each key carries a role-specific prefix so a misplaced key can be rejected at the request boundary:

  • exp_ + 32 lowercase hex characters
  • hid_ + 32 lowercase hex characters
  • cnf_ + 32 lowercase hex characters

If a single opaque session reference were enough to redeem a token, anyone who saw that reference could redeem on the user’s behalf. Splitting the proof three ways means an attacker has to compromise three different vantage points at the same time:

  • Without hiddenKey — an attacker who steals the URL the user is visiting still cannot redeem. The hidden key never leaves your server.
  • Without exposureKey — an attacker who breaches your server still cannot redeem someone else’s session, because each pending session’s exposure key is bound to the specific browser it was sent to.
  • Without confirmationKey — neither party can redeem a session the user never actually completed. The confirmation key is only minted by via.sudomimus.com after a real passkey or OTP challenge succeeds.
/establish ────► exposureKey + hiddenKey
│ │
▼ ▼
to browser stay on backend
via.sudomimus.com ──► user passes challenge
confirmationKey ──► to your callback
/redeem ◄──── exposureKey + hiddenKey + confirmationKey
accessToken + refreshToken

After /redeem succeeds, all three keys are consumed and cannot be reused. A second redemption with the same triple fails — even if the application repeats the request.

If you are familiar with the OAuth authorization-code flow, the rough analogues are:

SudomimusOAuth 2.0
exposureKeystate parameter (loosely)
confirmationKeyauthorization code
hiddenKeyno direct equivalent

OAuth relies on the long-lived client_secret to authenticate the token exchange. Sudomimus instead uses a per-session hiddenKey that is fresh on every /establish call. Leaking one session’s hidden key compromises that single redemption — not every redemption your application ever performs.

This is the same idea as proof-of-possession tokens: prefer short-lived, narrowly-scoped secrets over long-lived shared ones.

If you actually want OAuth/OIDC semantics, Sudomimus also runs a standard OIDC provider at oidc.sudomimus.com. The three-key model is what powers the Connect protocol underneath; relying parties using OIDC do not see it directly.

Aside from the three per-session keys, each application has two RSA-2048 keypairs that are long-lived:

  • Token-signing keypair — Sudomimus signs access and refresh JWTs with its private half; the application verifies signatures with the public half (fetched from POST /info).
  • Client-auth keypair — the application signs every /establish request with its private half (delivered once at application creation and at each rotation); Sudomimus verifies with the stored public half.

Neither of these is part of the “three-key” model. They authenticate the channel between Sudomimus and the application; the three per-session keys authenticate a single login. The details are in Tokens and verification.