Skip to content

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

  1. Parse logout_token as a JWT and verify its signature against the issuer's JWKS (https://id.trustnet.app/.well-known/jwks.json) with RS256.
  2. Validate:
  3. iss is exactly your issuer;
  4. aud contains your client_id;
  5. iat is recent;
  6. the events claim contains http://schemas.openid.net/event/backchannel-logout;
  7. sid is present;
  8. nonce is absent.
  9. Match sid to the sid you stored from that session's ID token and terminate the matching local session.
  10. Return 200 (any 2xx is 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 POST with Content-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_id as 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.
  • subs contains PPIDs only — never internal identifiers. Purge by sub.

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.