Skip to content

fix(auth): resolve concurrent login session race condition#16052

Open
brick-pixel wants to merge 1 commit intopayloadcms:mainfrom
brick-pixel:fix/concurrent-login-session-race
Open

fix(auth): resolve concurrent login session race condition#16052
brick-pixel wants to merge 1 commit intopayloadcms:mainfrom
brick-pixel:fix/concurrent-login-session-race

Conversation

@brick-pixel
Copy link
Copy Markdown

What?

Fix a race condition where concurrent login requests cause session loss, resulting in valid tokens returning no user from /me.

Why?

When multiple login requests execute simultaneously for the same user, addSessionToUser reads the sessions array from the in-memory user object. Each concurrent request sees the same stale snapshot. The last write wins, silently discarding sessions created by earlier requests. Tokens from lost sessions then fail /me checks — the server returns 200 but with user: null.

This is reproducible with a simple concurrent login test (see linked issue for repro repo).

How?

1. Moved transaction boundary earlier in loginOperation (login.ts)

The transaction now wraps the entire login flow including user lookup, so the read-modify-write on sessions is serialized per user.

2. Fresh DB read in addSessionToUser (sessions.ts)

Instead of relying on the stale in-memory user.sessions, the function now reads the current sessions directly from the database within the transaction before appending the new session. Only the sessions field is written back to minimize the update surface.

3. Same pattern applied to revokeSession (sessions.ts)

Revoke also reads fresh session data before filtering, preventing stale-data bugs on concurrent logout/error paths.

4. Integration tests (test/auth/int.spec.ts)

Two new test cases verify that concurrent logins preserve all sessions and that every resulting token independently works with /me.

Diff summary

File Change
packages/payload/src/auth/operations/login.ts Move initTransaction before user lookup
packages/payload/src/auth/sessions.ts Fresh DB read before session append/revoke
test/auth/int.spec.ts 2 concurrent login test cases

+183 / -21 lines across 3 files

Fixes #16027

Concurrent login requests could cause session loss due to a
read-modify-write race in addSessionToUser. When multiple logins
executed simultaneously, each read the same stale sessions array
from the in-memory user object. The last write won, silently
discarding sessions created by earlier concurrent requests. Tokens
from lost sessions then failed /me checks with no user returned.

Changes:
- Move transaction start before user lookup in loginOperation so
  the entire login flow is atomic
- Read fresh session data from database in addSessionToUser instead
  of relying on the potentially stale in-memory user object
- Update only the sessions field to minimize write surface
- Apply the same fresh-read pattern to revokeSession
- Add integration tests for concurrent login session preservation
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Concurrent login: /api/users/me returns 200 but user is missing (Bearer token)

1 participant