Skip to content

Ending sessions

This page helps you end a user’s session, and know what that cuts off.

A session ends in one of four ways: someone ends it from the admin console or the API, the user signs out, the user’s credentials change, or a different user signs in on the same browser. And when an app redeems an authorization code twice, the auth server ends the session the code came from if it had refresh tokens to revoke: see auth_code_reuse_detected in the audit log. Each is a security action rather than housekeeping, so each one decides what happens to the tokens the session handed out. A session that merely expires is different: it revokes nothing.

  1. Find the session. A user sees their own under Account, Sessions in the admin console. An administrator sees a user’s on the user’s Sessions tab, and every session signed in to a client on the client’s User sessions tab.

  2. Choose End session beside it, and confirm.

    The same goes through the API: an administrator calls DELETE /api/v1/admin/user-sessions/{id}, and a user calls DELETE /api/v1/account/sessions/{id} for one of their own.

  3. The browser that held the session has to sign in again, and the tokens the session handed out stop working, as below.

What What happens
The session It’s deleted. The browser that held it has to sign in again
Refresh tokens from it Revoked, offline ones included
Authorization codes from it Revoked, so a code that was issued but not redeemed yet can’t be exchanged
The user’s other sessions Untouched

That’s what sets ending a session apart from one expiring. An offline refresh token is meant to outlive the session it came from, so it keeps working when that session times out. Ending the session revokes it on purpose.

Each one leaves two entries in the audit log: terminated_user_session, which records what was revoked, and deleted_user_session, the record that the session is gone. Count terminated_user_session to know how many sessions were ended.

A sign-in can’t slip past an ended session. If someone was partway through signing in to an app on the strength of the session you ended, the sign-in goes back to the password step rather than quietly starting a new session. That holds on the consent screen too: just before it issues the code, the auth server checks the session again, so a sign-in left waiting there goes back to the password step as well, and so does one whose session timed out while the screen was open.

A silent request can’t show a page, so it gets login_required instead, and your app decides when to send the user to sign in. Two things withhold that error: a client that registered itself is never sent one, and neither is a redirect URI no longer registered on the client. A silent request that doesn’t come back is then the signal. See The app gets no error back.

Your app signs a user out by sending them to /auth/logout. Signing out revokes nothing: no refresh token and no authorization code is revoked. What it ends depends on what the request carries:

  • With an id_token_hint, the auth server removes your app from the session the hint names. The session itself ends only when no other client is using it, so the user stays signed in to the others.
  • Without one, once the user confirms on the sign-out page, the whole session ends.

Either way the browser’s cookie is cleared, so its next sign-in starts from the password. A normal refresh token stops working once the session behind it has ended. Offline refresh tokens aren’t tied to a session and keep working.

A sign-out with a confirmed hint leaves deleted_user_session_client in the audit log, for your app leaving the session, and also logout when yours was the session’s last client and the session ended. One without a hint leaves logout, even when there was no session, and deleted_user_session when there was one.

When a user’s credentials change, their sessions and refresh tokens stop being usable:

Action What happens reason
The user resets a forgotten password Every session of theirs is ended and every refresh token revoked password_reset
The user changes their own password The same, except the session they’re changing it from password_change
An administrator sets the user’s password Every session of theirs is ended and every refresh token revoked admin_password_set
An administrator disables the user The same. Enabling them again doesn’t bring the sessions back account_disabled

That covers every grant, offline refresh tokens whose session has already expired and tokens from the password grant included, and an authorization code that was issued but not redeemed yet can’t be exchanged any more. Each change leaves a revoked_user_auth_state entry in the audit log, with the reason above and the sessions and refresh tokens it ended.

A user changing their own password normally stays signed in to the app they did it from. If that app refreshes at the same moment as the change, it may be asked to sign in again.

These end nothing:

  • Changing an email address. The change already asks for the current password, so signing other devices out would protect nothing that person couldn’t do again. Tokens issued before it carry the old email claim until they’re refreshed or expire.
  • Setting up or removing two-factor authentication. Each session is checked again for a second factor before it’s used for a level above urn:goiabada:level1, and removing it also lowers each session to what a password alone reaches. See ACR and AMR. For a lost or stolen device, end the user’s sessions as well: see A user who lost their authenticator.

A different user signs in on the same browser

Section titled “A different user signs in on the same browser”

A shared or borrowed browser can reach the sign-in page still holding someone else’s session. That happens whenever a client sends prompt=login or an id_token_hint naming another user, and whenever the session there has stopped being valid.

When the person who signs in is a different user, the browser has changed hands:

What What happens
The previous user’s session Ended, as if someone had ended it: deleted, with its refresh tokens and unredeemed authorization codes revoked
The new user Gets a session of their own, never the one that was there
The previous user’s other devices Untouched. Only what this browser’s session handed out is revoked

The grants that session held were reachable from this browser, and the browser is now someone else’s. An offline refresh token from it would otherwise keep working in the background indefinitely.

Nothing carries over. If the client asks for a one-time code, the new user is asked for one, however the previous user signed in, and the acr and amr in their tokens describe only what they did.

Each handover leaves a cross_user_session_replaced entry in the audit log, naming both users and the session that was ended, beside the terminated_user_session and deleted_user_session entries for that session and the started_new_user_session entry for the new one. It’s the entry that tells a browser changing hands apart from an administrator ending a session.

An access token is checked by signature, and an API doesn’t ask the auth server about it, so nothing on this page can take back one already issued:

  • The auth server’s own endpoints, /userinfo and the Admin and Account APIs, refuse an access token whose session has ended or expired, or whose user was disabled or changed their credentials, on the very next request.
  • An access token from an offline grant carries no sid, so ending the session it came from doesn’t reach it, even at the auth server’s own endpoints. A credential change does.
  • Your own APIs, checking the signature alone, accept the token until it expires.

That’s inherent to signed tokens, and it’s the main reason to keep access tokens short-lived: the token lifetime, 5 minutes in a new install, is the longest a token can outlive any of this.