Log out
TrustNet supports three different endings, and they are not interchangeable:
| Action | What ends | Use it when |
|---|---|---|
| RP-initiated logout | The user's TrustNet session | The user clicks "Log out" in your app. |
| Back-channel logout | Your app's session for this RP | TrustNet ends the session, revokes access, or the user switches persona. |
| Token revocation | The tokens | You no longer need a token, or you are responding to a compromise. |
Logging the user out of TrustNet does not revoke your RP's consent, and revoking a token does not by itself end the user's browser session.
RP-initiated logout
Redirect the browser to the end-session endpoint with the ID token as a hint:
https://id.trustnet.app/oauth2/sessions/logout
?id_token_hint=<id_token>
&post_logout_redirect_uri=<registered-post-logout-uri>
&state=<opaque-state>
post_logout_redirect_uri must exactly match one of the post-logout redirect
URIs you registered. On success, TrustNet ends the session and redirects back.
It also notifies clients with back-channel logout configured.
Back-channel logout (required)
Registration requires a back-channel logout URI. TrustNet calls it server-to-server when the user's session ends, access is revoked, or the user switches persona; this is how your app learns that its session is no longer valid without relying on the browser.
What TrustNet sends
A POST with Content-Type: application/x-www-form-urlencoded and one
parameter:
logout_token=<compact JWT>
What your endpoint must do
- Parse
logout_tokenas a JWT and verify its signature against the issuer's JWKS (https://id.trustnet.app/.well-known/jwks.json) withRS256. - Validate:
issis exactly your issuer;audcontains yourclient_id;iatis recent;- the
eventsclaim containshttp://schemas.openid.net/event/backchannel-logout; sidis present;nonceis absent.- Match
sidto thesidyou stored from that session's ID token and terminate the matching local session. - Return
200(any2xxis accepted). Do not redirect; this is not a browser request.
The logout token carries sid only — no sub. Do not expect to identify the
user from it; match the session id you already stored. An unknown sid is not
an error: return 200 and do nothing. Delivery is not guaranteed to be
exactly-once, so make the handler idempotent.
Example validation with jose:
import { createRemoteJWKSet, jwtVerify } from "jose";
const JWKS = createRemoteJWKSet(
new URL("https://id.trustnet.app/.well-known/jwks.json"),
);
const { payload } = await jwtVerify(logoutToken, JWKS, {
issuer: "https://id.trustnet.app",
audience: clientId,
algorithms: ["RS256"],
});
const events = payload.events ?? {};
if (!events["http://schemas.openid.net/event/backchannel-logout"]) {
throw new Error("not a back-channel logout token");
}
if (!payload.sid || payload.nonce !== undefined) {
throw new Error("invalid logout token");
}
await terminateSessionBySid(payload.sid);
The OIDC Back-Channel Logout specification is the normative reference: https://openid.net/specs/openid-connect-backchannel-1_0.html.
Persona switches
When a user switches persona at your RP, TrustNet sends a back-channel logout so you can re-fetch claims under the new persona instead of silently swapping values under an existing session. Treat it like any other logout: end the session, then re-run authorization if the user wants to continue.
Token revocation
Revoke an access or refresh token at the revocation endpoint when you no longer need it:
curl -sS -X POST "https://id.trustnet.app/oauth2/revoke" \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-H "Content-Type: application/x-www-form-urlencoded" \
--data-urlencode "token=$REFRESH_TOKEN" \
--data-urlencode "token_type_hint=refresh_token"
A revoked token is inactive immediately; a refresh token no longer exchanges,
and a token used at UserInfo or the machine plane gets 401 invalid_token.
Revocation is idempotent: revoking an already-invalid token still returns 200.
Users can also revoke your RP's access from their TrustNet dashboard. That invalidates the tokens and triggers both the back-channel logout and the optional webhook below.
Erasure notifications
If you registered a purge webhook URL, TrustNet sends a signed
server-to-server notification when a user revokes access
(access.revoked / user_revoke) or erases their data
(account.erased / user_erasure). It is not a logout; it tells you to purge
your copy of the user's data.
- The request is a
POSTwithContent-Type: application/jose. The body is a compact JWS over this JSON payload:
json
{
"event": "account.erased",
"client_id": "<client_id>",
"subs": ["<PPID>"],
"reason": "user_erasure",
"delivery_id": "<idempotency key>"
}
- Verify the JWS against the machine-plane JWKS:
https://rp.trustnet.app/api/v1/webhook/jwks. - Use
delivery_idas your idempotency key: delivery is at-least-once. - Return any
2xx. Failure is retried with backoff for about an hour; after that the delivery is recorded as failed. subscontains PPIDs only — never internal identifiers. Purge bysub.
The webhook is optional at registration, but it is the only automated way to learn about an erasure. Without it, the user's data remains until you receive the request through another channel.