diff --git a/CHANGELOG.md b/CHANGELOG.md index afbf34f3c..d98912479 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +### 1.2.x [unrealeased] + +* Fix a race condition in creation of AccessToken with external oauth2 server. + ### 1.2.0 [2018-06-03] * **Compatibility**: Python 3.4 is the new minimum required version. diff --git a/oauth2_provider/oauth2_validators.py b/oauth2_provider/oauth2_validators.py index 450a04fb5..2385be055 100644 --- a/oauth2_provider/oauth2_validators.py +++ b/oauth2_provider/oauth2_validators.py @@ -332,20 +332,15 @@ def _get_token_from_authentication_server( scope = content.get("scope", "") expires = make_aware(expires) - try: - access_token = AccessToken.objects.select_related("application", "user").get(token=token) - except AccessToken.DoesNotExist: - access_token = AccessToken.objects.create( - token=token, - user=user, - application=None, - scope=scope, - expires=expires - ) - else: - access_token.expires = expires - access_token.scope = scope - access_token.save() + access_token, _created = AccessToken\ + .objects.select_related("application", "user")\ + .update_or_create(token=token, + defaults={ + "user": user, + "application": None, + "scope": scope, + "expires": expires, + }) return access_token @@ -362,27 +357,11 @@ def validate_bearer_token(self, token, scopes, request): try: access_token = AccessToken.objects.select_related("application", "user").get(token=token) - # if there is a token but invalid then look up the token - if introspection_url and (introspection_token or introspection_credentials): - if not access_token.is_valid(scopes): - access_token = self._get_token_from_authentication_server( - token, - introspection_url, - introspection_token, - introspection_credentials - ) - if access_token and access_token.is_valid(scopes): - request.client = access_token.application - request.user = access_token.user - request.scopes = scopes - - # this is needed by django rest framework - request.access_token = access_token - return True - self._set_oauth2_error_on_request(request, access_token, scopes) - return False except AccessToken.DoesNotExist: - # there is no initial token, look up the token + access_token = None + + # if there is no token or it's invalid then introspect the token if there's an external OAuth server + if not access_token or not access_token.is_valid(scopes): if introspection_url and (introspection_token or introspection_credentials): access_token = self._get_token_from_authentication_server( token, @@ -390,15 +369,17 @@ def validate_bearer_token(self, token, scopes, request): introspection_token, introspection_credentials ) - if access_token and access_token.is_valid(scopes): - request.client = access_token.application - request.user = access_token.user - request.scopes = scopes - - # this is needed by django rest framework - request.access_token = access_token - return True - self._set_oauth2_error_on_request(request, None, scopes) + + if access_token and access_token.is_valid(scopes): + request.client = access_token.application + request.user = access_token.user + request.scopes = scopes + + # this is needed by django rest framework + request.access_token = access_token + return True + else: + self._set_oauth2_error_on_request(request, access_token, scopes) return False def validate_code(self, client_id, code, client, request, *args, **kwargs):