Managing sessions
A login is the start of a session, not the end of the integration. Session API provides four endpoints for the rest of the ordinary application session lifecycle: /refresh, /introspect, /logout, and /revoke-all. This page is the single reference for all of them.
For application code, prefer the Session SDK package for your language. See the SDK overview before hand-writing these HTTP calls.
If you’re using the OIDC flow, see also /end-session in the OIDC guide — it has a related but narrower purpose.
At a glance
Section titled “At a glance”| Endpoint | Authenticates with | Idempotent | Scope of effect |
|---|---|---|---|
POST /refresh | The refresh token itself | No (rotates the refresh token — each token works exactly once) | One session |
GET /userinfo | The access token as Bearer | Read-only | One session’s current shared profile |
POST /introspect | The access token itself | Read-only | One session |
POST /logout | The refresh token itself | Yes (calling twice returns revoked: true both times) | One session |
POST /revoke-all | Client-auth JWT (RS256) | Yes | All sessions for an account, scoped to the calling app |
None of these endpoints require setting up new infrastructure — they reuse the keys you already have from your initial integration.
/refresh — extend a session
Section titled “/refresh — extend a session”Exchange a refresh token for a fresh access token and a new refresh token. Refresh is strict rotation (OAuth 2.1 BCP §4.14.2): the presented signed version is consumed while the same logical ApplicationSession keeps its stable payload sid, stores a new jti, and increments payload rotationVersion. Store the new refreshToken and use it for the next refresh. Re-presenting a stale version is treated as compromise and terminally revokes that session. Near-simultaneous requests with the same version (for example, two browser tabs) are the exception: during the short grace window they adopt the exact winning version instead of advancing again or logging the user out. Reusing it after that window still triggers compromise, so always store and send the latest token.
curl -X POST https://session-api.sudomimus.com/refresh \ -H "Content-Type: application/json" \ -d '{ "refreshToken": "..." }'Response:
{ "accessToken": "<JWT>", "refreshToken": "<rotated JWT>", "claims": { "email": { "requirement": "REQUIRED", "state": "GRANTED" }, "firstName": { "requirement": "OPTIONAL", "state": "GRANTED" }, "lastName": { "requirement": "OFF", "state": "UNKNOWN" }, "staticAvatar": { "requirement": "SYNTHETIC_ONLY", "state": "UNKNOWN" }, "animatedAvatar": { "requirement": "OFF", "state": "UNKNOWN" } }}Persist the rotated refreshToken, replacing the one you just used — the old one is now invalid. The claims block is the same per-claim view returned by /redeem — see the claims block for how to read it.
Auth: none — possession of the refresh token is the credential.
The new access token’s TTL is the one resolved on the original /redeem (or /direct-issue/*, or OIDC /token). It is not re-resolved on refresh.
Refresh can fail on claims
Section titled “Refresh can fail on claims”/refresh is not only a success-or-revoke endpoint. If, since the last token was minted, a required claim has stopped being satisfied — the developer escalated a claim policy from optional to required, or the user revoked a grant — the refresh is rejected with ClaimConsentRequired rather than minting a token missing that required claim.
Recovery depends on the client, because /refresh itself cannot collect consent:
- Native clients (Steam / AccessKey) recover by re-running the original direct-issue, which returns an Errand handoff for the user to grant consent.
- Browser applications recover by sending the user through an ordinary interactive login again.
This is rare in practice — it only happens when a policy or grant changes mid-session — but build your refresh path to surface a re-authentication prompt rather than treating every refresh failure as a hard logout.
/introspect — is this token still valid?
Section titled “/introspect — is this token still valid?”Ask Sudomimus about the current status of an access token. Use this when you want to invalidate sessions promptly across services — e.g. when a user clicks “log out everywhere” and you need other tabs or services to notice within a bounded time.
curl -X POST https://session-api.sudomimus.com/introspect \ -H "Content-Type: application/json" \ -d '{ "accessToken": "..." }'Response:
{ "status": "active", "recommendedRecheckSeconds": 600}status is one of:
"active"— thesidresolves to an ACTIVE, live-authority ApplicationSession."revoked"— the session is terminally revoked or one of its authority bindings is no longer current."expired"— the fixed ApplicationSession expiry has passed."not_found"— the token is invalid or itssiddoes not resolve to a matching session.
recommendedRecheckSeconds is how long Sudomimus suggests you may cache the result before re-introspecting. It is always 600 seconds today. Treat the access token’s own exp claim as the upper bound on its usable lifetime; introspection is for catching early revocation.
Auth: none — the access token is self-authenticating. Anyone holding the token can ask whether it is still valid; nothing else is required.
When to call introspect
Section titled “When to call introspect”Local signature verification covers correctness; introspection covers freshness. A reasonable pattern:
- Verify the access token’s signature and
expclaim on every request (cheap, local). - Call
/introspectopportunistically — once per N minutes per session, on a background job, or when a user-visible state change suggests it.
You do not need to introspect on every request. Doing so would defeat the point of having a signed token in the first place.
/logout — invalidate a single session
Section titled “/logout — invalidate a single session”Terminally revoke the ApplicationSession identified by one genuine refresh-token version. Its access tokens stop being reported as active by /introspect, and every later /refresh for that sid fails.
curl -X POST https://session-api.sudomimus.com/logout \ -H "Content-Type: application/json" \ -d '{ "refreshToken": "..." }'Response:
{ "revoked": true }revoked: true— the session was revoked (or was already terminal; calling twice is fine).revoked: false— the token is invalid or not found.
Auth: none — possession of the refresh token authorizes logging out that session (RFC 7009 style).
/revoke-all — revoke every session for an account
Section titled “/revoke-all — revoke every session for an account”Advance the account/application revocation authority, then best-effort revoke the currently enumerated application-session rows. Every older session becomes unusable even if cleanup does not enumerate its row. Use this for account-takeover incident response, “log me out of all devices”, or support-initiated session termination.
The account is identified by its sector subject — payload sub on the application’s access token and the value you key users on. Refresh tokens carry no user identifier.
curl -X POST https://session-api.sudomimus.com/revoke-all \ -H "Content-Type: application/json" \ -H "Authorization: SudomimusClientJWT $SUDOMIMUS_CLIENT_AUTH_JWT" \ -d '{ "subject": "sub_9SQ5535CRWNDDM2T" }'Response:
{ "revoked": true }revoked: true acknowledges the request. Cleanup results are deliberately private because they are not the source of revocation authority. Unknown and out-of-sector subjects return the same acknowledgement so the endpoint does not reveal whether the subject exists.
Auth: a client-auth JWT with aud = "sudomimus-session". The scope of the action is implicit — only sessions issued to that account within the calling application are touched. You cannot use one application’s client-auth key to revoke sessions in another application.
Putting it together — a typical session lifecycle
Section titled “Putting it together — a typical session lifecycle” /redeem ──► access (3h) refresh (30d) │ │ │ │ ▼ │ used on every request │ │ │ ▼ expires │ /refresh ◄──────────────┤ new access + rotated refresh │ │ ▼ │ │ │ user clicks "log out" ──► /logout │ ▼ revoked │ /introspect ──► "revoked"For the OIDC variant of refresh, see OIDC relying parties — Refresh.