diff --git a/swh/auth/django/backends.py b/swh/auth/django/backends.py --- a/swh/auth/django/backends.py +++ b/swh/auth/django/backends.py @@ -21,7 +21,12 @@ oidc_user_from_decoded_token, oidc_user_from_profile, ) -from swh.auth.keycloak import ExpiredSignatureError, KeycloakOpenIDConnect +from swh.auth.keycloak import ( + ExpiredSignatureError, + KeycloakError, + KeycloakOpenIDConnect, + keycloak_error_message, +) def _update_cached_oidc_profile( @@ -194,6 +199,15 @@ except UnicodeEncodeError as e: sentry_sdk.capture_exception(e) raise ValidationError("Invalid bearer token") + except KeycloakError as ke: + error_msg = keycloak_error_message(ke) + if error_msg == "invalid_grant: Offline user session not found": + error_msg = ( + "Bearer token expired after a long period of inactivity; " + "please generate a new one." + ) + sentry_sdk.capture_exception(ke) + raise AuthenticationFailed(error_msg) except Exception as e: sentry_sdk.capture_exception(e) raise AuthenticationFailed(str(e)) diff --git a/swh/auth/tests/django/test_drf_bearer_token_auth.py b/swh/auth/tests/django/test_drf_bearer_token_auth.py --- a/swh/auth/tests/django/test_drf_bearer_token_auth.py +++ b/swh/auth/tests/django/test_drf_bearer_token_auth.py @@ -3,11 +3,22 @@ # License: GNU Affero General Public License version 3, or any later version # See top-level LICENSE file for more information +import json + from django.contrib.auth.models import AnonymousUser, User +from django.core.cache import cache import pytest from swh.auth.django.models import OIDCUser from swh.auth.django.utils import reverse +from swh.auth.keycloak import KeycloakError + + +@pytest.fixture +def keycloak_oidc(keycloak_oidc): + # ensure django cache is cleared before each test + cache.clear() + return keycloak_oidc @pytest.mark.django_db @@ -107,3 +118,34 @@ request = response.wsgi_request assert isinstance(request.user, AnonymousUser) + + +@pytest.mark.django_db +def test_drf_oidc_bearer_token_expired_token(keycloak_oidc, api_client): + url = reverse("api-test") + + oidc_profile = keycloak_oidc.login() + refresh_token = oidc_profile["refresh_token"] + + api_client.credentials(HTTP_AUTHORIZATION=f"Bearer {refresh_token}") + + kc_error_dict = { + "error": "invalid_grant", + "error_description": "Offline user session not found", + } + + keycloak_oidc.refresh_token.side_effect = KeycloakError( + error_message=json.dumps(kc_error_dict).encode(), response_code=400 + ) + + response = api_client.get(url) + expected_error_msg = ( + "Bearer token expired after a long period of inactivity; " + "please generate a new one." + ) + + assert response.status_code == 403 + assert expected_error_msg in json.dumps(response.data) + + request = response.wsgi_request + assert isinstance(request.user, AnonymousUser) diff --git a/swh/auth/tests/django/test_views.py b/swh/auth/tests/django/test_views.py --- a/swh/auth/tests/django/test_views.py +++ b/swh/auth/tests/django/test_views.py @@ -245,7 +245,6 @@ # should render an error page response = client.get(login_url) - print(response.content) assert response.status_code == 500 request = response.wsgi_request