Skip to content

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.

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.

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.

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.

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.

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.