New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix race condition for concurrent /auth/refresh
calls
#22433
Conversation
🦋 Changeset detectedLatest commit: 0fd5293 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
/auth/refresh
calls
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this PR prevents the session id (aka refresh token) from changing for session mode authentication we should prevent people from extracting the "session id / refresh token" from the session JWT and refreshing that like a regular json
refresh token
As this will result in the same "invalid token" situation as the race condition currently does
Could you help me understand why this would be necessary? If a user should do so, they certainly know what they are doing. |
Co-authored-by: Pascal Jufer <pascal-jufer@bluewin.ch>
One being functional, this is an "unintended feature" refreshing as session token as So from a functional / footgun pov I believe we should be intentional with authentication and only be allowing this if there is a valid use-case to support that which outweighs the potential risks. If this is a feature we want to support I'd be more in favor of intentionally implementing this in the login endpoint returning new credentials using a valid access_token and add proper documentation that this is possible.
True but we also have to consider malicious actors, while this is not directly exploitable out of the box however having an unused / undocumented feature in authentication inherently adds risk of abuse. A basic example would be if any token during the session gets leaked (expired or not) can then be used to create a new session, locking out the current user and persisting access to the platform indefinitely. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good to me so far:
- I'd even say it kinda makes more sense to not change the session identifier
- The session is still invalidated if the user logs-out
- As you mentioned, the session/refresh token will still expire when the user doesn't refresh
However, to @br41nslug's point, I agree we should make it more secure, by preventing usage of the session refresh token via other modes, ensuring the JWT expiration (SESSION_COOKIE_TTL
) still takes place 👍
This would probably mean to add a new field (type
or similar) to the directus_sessions
collection, which we can then compare against. This would go well with the middleware changes in #22327.
I was considering something in that direction too 🤔 as i dont see many other options of telling if the specific session getting refreshed was intended to be stateless or stateful. |
Yeah, another option could be to simply prefix session refresh token by something like |
Interesting idea 🤔 that does indeed feel risky as the tokens are auto-generated and could theoretically generate one with that exact prefix (or any other prefix that consists of characters that are used by the nanoid generator) |
After some discussion it looks like this frontend solution #22457 will take priority over the authentication change done here. |
Scope
In #22360 we uncovered that concurrent requests to the
/auth/refresh
endpoint end up causing a race condition where the user get's logged out if following happens:Scenario 1:
/auth/refresh
with a valid session cookie and session identifier (which is calledrefreshToken
in the code)token_1
/auth/refresh
with a the same session cookie and session identifiertoken_1
token_1
is in thedirectus_sessions
tabletoken_2
, updates that in thedirectus_sessions
table, and receives a new session cookie withsession: token_2
token_1
is not in thedirectus_sessions
table anymoreScenario 2:
Similar two Scenario 1, but the verification are more interleaved due to the async nature of the database / auth provider / hook calls we're making.
token_1
is in thedirectus_sessions
tabletoken_2
and sets the session cookietoken_3
and sets the session cookietoken_2
set.Since we're using the
refreshToken
as a session identifier, wrapped in the session cookie, in thesession
mode, it should safe to not update it in the DB. The signed session cookie with the expiry time coded in takes care of invalidating the session afterSESSION_COOKIE_TTL
andREFRESH_TOKEN_TTL
takes care of invalidating the session stored in the DB.Not rotating the
refreshToken
(really the session identifier) should not impact the security as far as IWhat's changed:
session
modePotential Risks / Drawbacks
Should be none
Review Notes / Questions
Fixes #22360