What we deliberately don't do
Five things this standard leaves out on purpose, and the reasoning for each.
A security review finds these faster than we can explain them, so they are published rather than discovered. Each is a decision with a reason, not an unfinished edge.
1. No sender-constraining (mTLS or DPoP)
Section titled “1. No sender-constraining (mTLS or DPoP)”Member tokens are bearer tokens. We do not bind them to a client certificate (RFC 8705) or a proof-of-possession key (RFC 9449).
The token’s exposure is already narrow: it lives 15 minutes (see the token exchange), exists only in Doshi’s memory on your page, and is minted from a credential — your assertion — that is itself sender-constrained by your private key and never enters a browser. Sender-constraining the member token would add a mandatory client-side key ceremony to every integration to shorten a window that is already short. It stays on the list to revisit if a client’s threat model needs it.
2. No OTP or 2FA of our own
Section titled “2. No OTP or 2FA of our own”We never re-challenge the member. You have already authenticated them, to whatever standard your regulator requires, and the assertion is the statement that you did.
A second factor from us would either duplicate yours or contradict it, and it would put us in the business of holding authentication factors for your customers — which is exactly what this standard exists to avoid.
3. No session kill switch
Section titled “3. No session kill switch”There is no API to terminate a live coach session, and no console button for one.
Ending a session means removing Doshi from your page; ending access means your backend declining to sign further assertions. Both take effect at the next renewal, so the outer bound is one member-token lifetime. A kill switch would have to reach into a token we deliberately made stateless, and it would buy less time than that bound already gives.
If a key is compromised rather than a session, that is a different lever: you pause integration in your console and minting stops immediately, for every key in that environment. See the revocation arithmetic.
4. No replay store
Section titled “4. No replay store”We do not persist jti values to detect a replayed assertion.
Replay defence guards a credential that can be intercepted. This one cannot: the
assertion travels backend-to-backend over TLS, never through a browser, and it lives
60 seconds. An attacker positioned to capture it is already positioned to do worse. We
do log every jti, so a replay is visible after the fact even though it is not blocked
in the moment.
5. No back-channel logout
Section titled “5. No back-channel logout”We implement no OIDC back-channel or front-channel logout endpoint, and we will not call one of yours.
There is no server-side session for such a signal to terminate — see §3. A back-channel protocol would be ceremony around a state that does not exist.
Two more absences worth naming, both for the same reason:
No verify endpoint. There is no “would this assertion be accepted?” API. It would be an oracle for probing our verifier, and a second error surface to keep in step with the real one. Integrate in Test: its rejections are the real rejections, with the same codes and the same descriptions as Live’s.
No published test fixtures. No canned expired / wrong-audience / unknown-key assertions to assert against. Same reasoning: a fixture set is a contract we would have to keep true, and Test already tells you the truth.