Refresh tokens and sessions
Keep users signed in with SenseCrypt refresh tokens — requesting offline_access, handling rotation and reuse detection, revoking sessions, and implementing logout against the OP browser session.
SenseCrypt keeps a short-lived OP browser session (the sc_op_session cookie, 8 hours by default and configurable per tenant) so a second application can be authorized without another face ceremony and prompt=none can answer silently. Everything longer-lived than that is about tokens. This guide covers refresh tokens, rotation, revocation, and logout — and how logout ends both.
Request a refresh token
A refresh token is issued only when you request the offline_access scope at the authorization step:
GET /v1/idp/oidc/authorize?client_id=...&scope=openid%20offline_access%20profile&...If you omit offline_access, no refresh token comes back — the access token stands alone until it expires. Refresh tokens are opaque (not JWTs); treat them as bearer secrets and store them securely.
Refresh an access token
Exchange the refresh token at the token endpoint:
POST /v1/idp/oidc/token
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token=<token>&client_id=<id>(Confidential clients also present their client credential per their registered auth method.) You get a new access token — and, when rotation is enabled, a new refresh token you must store in place of the old one.
Rotation and reuse detection
Depending on the app's configuration — and always for SPA (public) clients — each refresh rotates the token and returns a successor. This is a security feature, but it has rules you must respect:
- Always store the newest refresh token returned and discard the previous one.
- Reusing an already-rotated token outside a short overlap window is treated as a breach: SenseCrypt revokes the entire token family and returns
invalid_grant. A stolen-and-replayed token thus takes out the legitimate session too — the safe failure. - Avoid racing refreshes. Two concurrent requests using the same refresh token can trip reuse detection. Serialize refreshes in your client, or rely on the short overlap window.
The access gate re-runs on every refresh
A refresh is not a free pass — it re-runs the full access gate and recomputes permissions:
- A user who has been suspended, deleted, un-enrolled, or removed from the app's groups fails closed on their next refresh.
- The
permissionsclaim is recomputed fresh on every refresh, so a revoked role or a removed group membership shrinks the next access token — you don't wait for expiry.
Absolute lifetime
Every token family has an absolute lifetime fixed at issuance that rotation can never extend. When it lapses, the user must sign in again (a fresh face ceremony). Plan your UX around a periodic re-authentication rather than indefinite sessions.
Revoke a session (RFC 7009)
To end a session server-side, revoke a token at the revocation endpoint:
POST /v1/idp/oidc/revoke
Content-Type: application/x-www-form-urlencoded
token=<refresh_or_access_token>&client_id=<id>- Revoking a token revokes the refresh-token family it belongs to.
- If the presented token parses as one of the client's access tokens, its
jtiis denylisted until it expires. - The endpoint always returns an empty
200, whether or not the token was found — there is no validity oracle. (Client authentication still applies: a bad client credential returnsinvalid_client.)
Log out (RP-Initiated Logout)
"Logout" means two things at once: ending the OP browser session behind the sc_op_session cookie (so the next /authorize runs a fresh face ceremony instead of answering silently) and revoking the refresh-token families the session left behind. Use the logout endpoint:
GET /v1/idp/oidc/logout?id_token_hint=<id_token>&post_logout_redirect_uri=<url>&state=<opaque>Key requirements:
- Send an
id_token_hint. It is what proves which application is asking, and it is the only route to apost_logout_redirect_uriredirect. Without one, SenseCrypt shows a confirmation page and — once the user confirms — signs them out and lands on its own signed-out page, never on your redirect. A request carrying apost_logout_redirect_uribut neither anid_token_hintnor aclient_idis refused with a 400 page and nothing is signed out. The hint is verified for signature and issuer, but an expired hint is accepted. - Logout revokes that user's live refresh-token families for every application the ended browser session signed in to, plus the application the
id_token_hintproves — not just the(subject, client)pair that asked. Signing out of one application therefore signs the user out of the others that shared the session. - Any
post_logout_redirect_urimust be on the client's dedicated post-logout allow-list — which is separate from the/authorizecallback allow-list. Otherwise it's rejected (never an open redirect).stateis round-tripped onto the redirect. - With no redirect URI, a static "You have been signed out" page is returned.
A note on access-token validity
Access tokens are self-contained JWTs — they remain valid until exp unless explicitly revoked (their jti denylisted, or their family revoked). Two implications:
- Keep access-token lifetimes short and refresh as needed, so that suspensions and permission changes take effect promptly.
- Your resource servers get immediate revocation only if they check the denylist (via the introspection endpoint) or if the access token expires soon. Local JWT verification alone will honor a token until
exp.
Eager revocation on lifecycle events
You rarely need to revoke tokens by hand for lifecycle changes — SenseCrypt does it eagerly:
- SCIM
active: false/ delete, account self-delete and an email change sever the identity plane: the user's device keys, passkeys, refresh-token families and OP browser sessions all go at once. - Group-member removal is an authorization change, so it stays on the session planes — the device key and passkey survive, because the user may still reach other applications through other groups, and the access gate is what closes the application they lost. It revokes the refresh-token families for the applications the user can no longer reach, and each of those applications that opted into the
access-revokedinitiator receives a back-channel logout token for every live browser session of theirs that had signed in to it (with no live session there is nothing to announce). A user left in no app-attached group also loses every remaining refresh-token family in the tenant and their OP browser session, and every application that session had signed in to which opted in is told. - Outstanding access tokens then read as inactive through introspection.
So removing a user from a group, or suspending them, ends their ability to refresh right away — you don't have to call the revocation endpoint yourself.
Related
- Tokens & sessions — token contents and signing.
- Groups and access — the gate that re-runs on refresh.
- Security best practices — where and how to store tokens.
- Error codes —
invalid_grantand revocation behavior.
Customize the sign-in experience
Brand the SenseCrypt sign-in page per OIDC application — organization name, primary and accent colors, and a logo you upload — so users see your identity, not a generic prompt.
Back-channel logout
Receive OIDC Back-Channel Logout tokens — registering the URL, choosing which session endings you are told about, the logout token's claims, delivery and retries, and the checks your endpoint must make.