Skip to content

Feature request: Logout must invalidate the current refresh session #1001

Description

@tickBit

What do you want to see in the API?

Logging out must invalidate the current refresh session on the server. Clearing browser cookies alone is insufficient because a previously copied refresh token could still be used to obtain new tokens.

The refresh endpoint could support both operations:

  • POST /auth/refresh rotates the current refresh token.
  • DELETE /auth/refresh revokes the current refresh session and clears the authentication cookies.

For web clients, the refresh token is read from an HttpOnly cookie. Other clients may provide it in the request body or a separately defined header.

The logout/revoke operation should:

  • Return 204 No Content on success.
  • Be idempotent.
  • Clear the web client’s access-token and refresh-token cookies.
  • Invalidate only the current session.
  • Work with both regular authentication and box authentication, as they share the same refresh endpoint and refresh-token handling.

Recommended token lifetimes:

  • Access token: approximately 15 minutes.
  • Refresh token inactivity timeout: approximately 7 days.
  • Refresh token absolute lifetime: approximately 30 days from the original sign-in.

The exact values should be configurable through separate environment variables, too. Rotating a refresh token must not extend the session beyond its original absolute expiration time.

How do you think this should work?

Refresh tokens should be associated with server-side sessions:

  1. A refresh session is created in the database during sign-in.
  2. The refresh token contains a unique session identifier, such as a sessionId or jti claim.
  3. Only a hash of the refresh token is stored in the database. The raw token must not be stored.
  4. POST /auth/refresh verifies that the session is active and has not exceeded its inactivity or absolute lifetime.
  5. Refresh-token rotation is atomic: the old token is invalidated and replaced with a new one.
  6. DELETE /auth/refresh revokes the current session before clearing the cookies.
  7. After revocation, the session’s refresh token can no longer be used to obtain new tokens.
  8. Reuse of an already rotated refresh token should revoke the affected session or token family.

The access and refresh tokens should have separate TTL configuration. Access tokens should be short-lived, while refresh sessions may remain valid longer through rotation.

Regular logout should not increment the profile-wide tokenVersion, because doing so would also invalidate other web, mobile, and box sessions belonging to the same profile. A global version or bulk session revocation can still be used for:

  • “Log out from all devices”
  • Password changes or resets
  • Account compromise
  • Other profile-wide security events

The refresh session should retain the authentication context required by both regular and box authentication. Claims such as box_id and box administrator information must be preserved securely during rotation.

Acceptance criteria

  • A refresh attempt made with a token after logout fails.
  • Logging out from one session does not invalidate the profile’s other web, mobile, or box sessions.
  • Regular authentication and box authentication use the same secure refresh and revocation logic.
  • Only one of two concurrent refresh requests using the same token can succeed.
  • Raw refresh tokens are not stored in the database.
  • Authentication cookies are cleared even when the session is already expired or revoked.
  • Access and refresh tokens have separate, configurable TTL values.
  • Refresh rotation does not extend the session beyond its original absolute expiration time.
  • Tests cover regular authentication, box authentication, rotation, concurrent refresh attempts, expiration, token reuse, and refresh attempts after logout.

Any additional info?

The server and the web version of the game are hosted on the same server. The refresh-token cookie should remain restricted to the /auth/refresh path so that the browser does not send it unnecessarily to other endpoints.

Using different HTTP methods on the same path allows the cookie scope to remain narrow:

  • POST /auth/refresh refreshes and rotates the tokens.
  • DELETE /auth/refresh revokes the session and performs logout.

Because cookie path matching is independent of the HTTP method, the browser sends the refresh-token cookie for both operations without exposing it to the rest of the /auth routes.

The current logout implementation only removes browser cookies. It does not invalidate a refresh token that may already have been copied. Server-side refresh sessions ensure that a revoked token cannot be used after logout.

Cookie expiration must also be calculated correctly. Express cookie maxAge values are relative durations in milliseconds, whereas JWT exp is an absolute Unix timestamp in seconds. Separate calculations should be used for JWT expiration and cookie lifetime.

About database usage

Token-version fields cannot identify an individual login session. A user may be signed in through multiple browsers, mobile devices, and box sessions at the same time. Incrementing a web- or app-specific token version would therefore invalidate several sessions even when the user only wants to log out from one device.
A separate refresh-session record represents one login on one device. This allows logout to revoke only the current session without affecting the user’s other sessions.
The database should store only a hash of the refresh token, not the token itself. If the database is exposed, the stored hash cannot be used directly to obtain new access tokens.
Separate session records also make it possible to rotate refresh tokens safely, detect reuse of old tokens, apply session-specific expiration, and remove expired sessions automatically. The profile-wide tokenVersion can still be used when all sessions must be invalidated, for example after a password change or when using “log out from all devices.”

Legacy refresh tokens and migration

Existing refresh tokens do not contain a session identifier and do not have a corresponding refresh-session record in the database. They cannot therefore be revoked or rotated safely on a per-session basis.
After the new session-based implementation is deployed, legacy refresh tokens should be rejected. Users with an existing session will need to sign in once again, after which a new refresh-session record and session-bound tokens will be created.
A claim such as tokenFormatVersion can be added to new refresh tokens to distinguish them explicitly from legacy tokens. Refresh requests should succeed only when:

  • The token uses the supported format.
  • It contains a valid session identifier.
  • The corresponding session exists and is active.
  • The presented token matches the hash stored for that session.
    This migration does not require changes to existing profile data. Only existing login sessions are affected; user accounts, player data, and box-related data remain unchanged.
    Rejecting legacy refresh tokens is preferred over automatically converting them because legacy tokens cannot provide session-level revocation or reliable reuse detection. Any client receiving an invalid-token response should clear its local authentication state and require the user to sign in again.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

featureNew feature to add

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions