From aede24bef889d9bc9b5947e23145245e5f2e6e12 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 2 Sep 2024 14:17:32 -0400 Subject: [PATCH 1/2] [pre-commit.ci] pre-commit autoupdate (#1475) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d240cdf98..b124c7342 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.6.2 + rev: v0.6.3 hooks: - id: ruff args: [ --fix ] From 62508b4a2dc563e850145675a7d92de0a730c255 Mon Sep 17 00:00:00 2001 From: Miriam Forner Date: Tue, 3 Sep 2024 15:53:17 +0100 Subject: [PATCH 2/2] Raise InvalidGrantError if no grant associated with auth code exists (#1476) Previously, when invalidating an authorization code after it has been used, if for whatever reason the associated grant object no longer exists, an uncaught exception would be raised - Grant.DoesNotExist. This could be caused by concurrent requests being made using the same authorization token. We now handle this scenario gracefully by catching Grant.DoesNotExist and returning an InvalidGrantError. --- AUTHORS | 1 + CHANGELOG.md | 1 + oauth2_provider/oauth2_validators.py | 13 +++++++++---- tests/test_oauth2_validators.py | 20 +++++++++++++++++++- 4 files changed, 30 insertions(+), 5 deletions(-) diff --git a/AUTHORS b/AUTHORS index 584ecf59c..ba9afa8da 100644 --- a/AUTHORS +++ b/AUTHORS @@ -119,3 +119,4 @@ pySilver Wouter Klein Heerenbrink Yaroslav Halchenko Yuri Savin +Miriam Forner diff --git a/CHANGELOG.md b/CHANGELOG.md index 3dfa94c4b..68d7f0081 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * Update middleware, validators, and views to use token checksums instead of token for token retrieval and validation. * #1446 use generic models pk instead of id. * Bump oauthlib version to 3.2.0 and above +* Update the OAuth2Validator's invalidate_authorization_code method to return an InvalidGrantError if the associated grant does not exist. ### Deprecated ### Removed diff --git a/oauth2_provider/oauth2_validators.py b/oauth2_provider/oauth2_validators.py index 78667fa0e..7cb1ecfd5 100644 --- a/oauth2_provider/oauth2_validators.py +++ b/oauth2_provider/oauth2_validators.py @@ -24,7 +24,7 @@ from jwcrypto import jws, jwt from jwcrypto.common import JWException from jwcrypto.jwt import JWTExpired -from oauthlib.oauth2.rfc6749 import utils +from oauthlib.oauth2.rfc6749 import errors, utils from oauthlib.openid import RequestValidator from .exceptions import FatalClientError @@ -318,10 +318,15 @@ def confirm_redirect_uri(self, client_id, code, redirect_uri, client, *args, **k def invalidate_authorization_code(self, client_id, code, request, *args, **kwargs): """ - Remove the temporary grant used to swap the authorization token + Remove the temporary grant used to swap the authorization token. + + :raises: InvalidGrantError if the grant does not exist. """ - grant = Grant.objects.get(code=code, application=request.client) - grant.delete() + try: + grant = Grant.objects.get(code=code, application=request.client) + grant.delete() + except Grant.DoesNotExist: + raise errors.InvalidGrantError(request=request) def validate_client_id(self, client_id, request, *args, **kwargs): """ diff --git a/tests/test_oauth2_validators.py b/tests/test_oauth2_validators.py index ca80aedb0..f499faf2d 100644 --- a/tests/test_oauth2_validators.py +++ b/tests/test_oauth2_validators.py @@ -9,9 +9,15 @@ from django.utils import timezone from jwcrypto import jwt from oauthlib.common import Request +from oauthlib.oauth2.rfc6749 import errors as rfc6749_errors from oauth2_provider.exceptions import FatalClientError -from oauth2_provider.models import get_access_token_model, get_application_model, get_refresh_token_model +from oauth2_provider.models import ( + get_access_token_model, + get_application_model, + get_grant_model, + get_refresh_token_model, +) from oauth2_provider.oauth2_backends import get_oauthlib_core from oauth2_provider.oauth2_validators import OAuth2Validator @@ -28,6 +34,7 @@ UserModel = get_user_model() Application = get_application_model() AccessToken = get_access_token_model() +Grant = get_grant_model() RefreshToken = get_refresh_token_model() CLEARTEXT_SECRET = "1234567890abcdefghijklmnopqrstuvwxyz" @@ -578,3 +585,14 @@ def test_validate_id_token_bad_token_no_aud(oauth2_settings, mocker, oidc_key): validator = OAuth2Validator() status = validator.validate_id_token(token.serialize(), ["openid"], mocker.sentinel.request) assert status is False + + +@pytest.mark.django_db +def test_invalidate_authorization_token_returns_invalid_grant_error_when_grant_does_not_exist(): + client_id = "123" + code = "12345" + request = Request("/") + assert Grant.objects.all().count() == 0 + with pytest.raises(rfc6749_errors.InvalidGrantError): + validator = OAuth2Validator() + validator.invalidate_authorization_code(client_id=client_id, code=code, request=request)