You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
While looking into why a user can stay logged in after signOut(), I found a
race between sign-out and the refetches SessionProvider fires on its own
(mount, window focus, polling, cross-tab broadcasts):
a GET /api/auth/session starts (poll, focus, ...)
signOut() completes and clears the client state
the GET from step 1 resolves last and puts the old session back
With the default JWT strategy it's worse than a UI glitch: the session
endpoint re-signs the token and sets a fresh rolling cookie on every valid
GET, so the late response can leave the browser holding a valid session
cookie after sign-out. Reload and you're still logged in. This matches
long-standing reports like #4612.
The fix keeps an AbortController on __NEXTAUTH:
signIn/signOut (and the WebAuthn signIn) abort any in-flight session
fetch before sending their POST, and once more after it returns
a session response is only applied if its fetch wasn't aborted or
superseded by a newer one (fresh controller per fetch, newest wins)
update() goes through the same guard
a failed sign-in replays the refetch it aborted, and an aborted initial
fetch keeps the status at "loading" instead of flashing "unauthenticated"
(that flash could trigger useSession({ required: true }) redirects under
StrictMode)
Two behavior changes worth calling out: a failed non-redirect signIn now
does one extra session GET (the replay), and update() resolves null when
it was superseded by an auth-state change.
What this can't fix from the client side: another tab's concurrent GET can
still get a rolling cookie, and JWT sessions can't be invalidated
server-side. Both are noted in code comments.
The tests reproduce the races deterministically by controlling fetch
resolution order — they fail without the fix. They're also the first React
client tests in this package, which is why the diff includes jsdom /
@testing-library/react / react-dom dev deps and a package-local vitest
config (the shared config aliases react to preact/compat, which breaks
@testing-library/react).
Next steps: Take a moment to review the security alert above. Review
the linked package source code to understand the potential risk. Ensure the
package is not malicious before proceeding. If you're unsure how to proceed,
reach out to your security team or ask the Socket team for help at
support@socket.dev.
Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.
Mark the package as acceptable risk. To ignore this alert only
in this pull request, reply with the comment
@SocketSecurity ignore npm/data-urls@5.0.0. You can
also ignore all packages with @SocketSecurity ignore-all.
To ignore an alert for all future pull requests, use Socket's Dashboard to
change the triage state of this alert.
Warn
Obfuscated code: npm rrweb-cssom is 90.0% likely obfuscated
Next steps: Take a moment to review the security alert above. Review
the linked package source code to understand the potential risk. Ensure the
package is not malicious before proceeding. If you're unsure how to proceed,
reach out to your security team or ask the Socket team for help at
support@socket.dev.
Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.
Mark the package as acceptable risk. To ignore this alert only
in this pull request, reply with the comment
@SocketSecurity ignore npm/rrweb-cssom@0.7.1. You can
also ignore all packages with @SocketSecurity ignore-all.
To ignore an alert for all future pull requests, use Socket's Dashboard to
change the triage state of this alert.
Warn
Obfuscated code: npm rrweb-cssom is 90.0% likely obfuscated
Next steps: Take a moment to review the security alert above. Review
the linked package source code to understand the potential risk. Ensure the
package is not malicious before proceeding. If you're unsure how to proceed,
reach out to your security team or ask the Socket team for help at
support@socket.dev.
Suggestion: Packages should not obfuscate their code. Consider not using packages with obfuscated code.
Mark the package as acceptable risk. To ignore this alert only
in this pull request, reply with the comment
@SocketSecurity ignore npm/rrweb-cssom@0.8.0. You can
also ignore all packages with @SocketSecurity ignore-all.
To ignore an alert for all future pull requests, use Socket's Dashboard to
change the triage state of this alert.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
☕️ Reasoning
While looking into why a user can stay logged in after
signOut(), I found arace between sign-out and the refetches
SessionProviderfires on its own(mount, window focus, polling, cross-tab broadcasts):
GET /api/auth/sessionstarts (poll, focus, ...)signOut()completes and clears the client stateWith the default JWT strategy it's worse than a UI glitch: the session
endpoint re-signs the token and sets a fresh rolling cookie on every valid
GET, so the late response can leave the browser holding a valid session
cookie after sign-out. Reload and you're still logged in. This matches
long-standing reports like #4612.
The fix keeps an AbortController on
__NEXTAUTH:signIn/signOut(and the WebAuthnsignIn) abort any in-flight sessionfetch before sending their POST, and once more after it returns
superseded by a newer one (fresh controller per fetch, newest wins)
update()goes through the same guardfetch keeps the status at "loading" instead of flashing "unauthenticated"
(that flash could trigger
useSession({ required: true })redirects underStrictMode)
Two behavior changes worth calling out: a failed non-redirect
signInnowdoes one extra session GET (the replay), and
update()resolvesnullwhenit was superseded by an auth-state change.
What this can't fix from the client side: another tab's concurrent GET can
still get a rolling cookie, and JWT sessions can't be invalidated
server-side. Both are noted in code comments.
The tests reproduce the races deterministically by controlling fetch
resolution order — they fail without the fix. They're also the first React
client tests in this package, which is why the diff includes jsdom /
@testing-library/react / react-dom dev deps and a package-local vitest
config (the shared config aliases react to preact/compat, which breaks
@testing-library/react).
🧢 Checklist
🎫 Affected issues
Related: #4612, #12354, #3995, #3991