Prevent racecondition on consuming refresh token. #558
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.
From checking to see whether the refresh token is valid in
validate_refresh_token
to when it's revoked insave_bearer_token
inoauth2_provider/oauth2_validators.py
, multiple requests can validate the same refresh token (where it's then stored in memory) and generate multiple access tokens from it. While this is allowed by the spec and explicitly configurable via theROTATE_REFRESH_TOKEN
, it seems like unexpected behavior.To reproduce this, a fully concurrent example should be used, e.g. the demo with postgres and gunicorn with multiple workers and concurrent requests. This script will return multiple access tokens:
Example output:
It looks like this existed before #543, but might have been made a bit worse in moving from the
.delete()
to marking the refresh token as revoked (note thatdelete()
can be called multiple times on an object without blowing up). I think this is related to #389, but that was solving a slightly different issue.This PR takes out a lock on the refresh token before revoking it and allowing the request to proceed. I was not sure how to reproduce the scenario in tests - currently travis uses sqlite, which can't reproduce the issue because it's not concurrent.