👔(oidc) consider urls as refreshable no matter the HTTP method

Currently, only `GET` method as considered as refreshable url as it could
be weird to return a redirect response for other kind of HTTP methods.
On our side, we assume to be always in a XHR context so we don't want
to be bothered by redirects and in case the session has expired
we returned a 401 status. So we can safely ignore the request HTTP method
to check if the url is refreshable.
This commit is contained in:
jbpenrath
2025-11-05 10:28:39 +01:00
parent 654ec35437
commit a9e2daa979
3 changed files with 43 additions and 7 deletions

View File

@@ -11,6 +11,7 @@ and this project adheres to
### Changed
- 🐛(joserfc) refactor JWT handling with joserfc library updates #35
- 👔(oidc) consider urls as refreshable no matter the HTTP method
## [0.0.17] - 2025-10-27

View File

@@ -12,13 +12,15 @@ import time
from urllib.parse import quote, urlencode
import requests
from django.contrib.auth import BACKEND_SESSION_KEY
from django.http import JsonResponse
from django.urls import reverse
from django.utils.crypto import get_random_string
from django.utils.module_loading import import_string
from mozilla_django_oidc.middleware import SessionRefresh
from rest_framework.status import HTTP_400_BAD_REQUEST, HTTP_401_UNAUTHORIZED
from lasuite.oidc_login.backends import get_oidc_refresh_token, store_tokens
from lasuite.oidc_login.backends import OIDCAuthenticationBackend, get_oidc_refresh_token, store_tokens
try:
from mozilla_django_oidc.middleware import (
@@ -83,6 +85,34 @@ class RefreshOIDCAccessToken(SessionRefresh):
query = urlencode(auth_params, quote_via=quote)
return f"{auth_url}?{query}"
def is_refreshable_url(self, request):
"""
Take a request and returns whether it triggers a refresh examination.
In the original implementation [1], the request method is checked to be
GET. This is relevant as if the session has expired, the user will be
redirected to a login page, that can be problematic for XHR requests.
Like the `finish` method documentation explains, in our implementation
we consider all requests as XHR requests, so in our case we can safely
ignore the request method check as in case of expired token,
a 401 status code will be always returned.
1. https://github.com/mozilla/mozilla-django-oidc/blob/774b140/mozilla_django_oidc/middleware.py#L96-L117
"""
# Do not attempt to refresh the session if the OIDC backend is not used
backend_session = request.session.get(BACKEND_SESSION_KEY)
is_oidc_enabled = True
if backend_session:
auth_backend = import_string(backend_session)
is_oidc_enabled = issubclass(auth_backend, OIDCAuthenticationBackend)
return (
request.user.is_authenticated
and is_oidc_enabled
and request.path not in self.exempt_urls
and not any(pattern.match(request.path) for pattern in self.exempt_url_patterns)
)
def is_expired(self, request):
"""Check whether the access token is expired and needs to be refreshed."""
if not self.is_refreshable_url(request):

View File

@@ -113,11 +113,12 @@ def test_basic_auth_disabled(oidc_settings): # pylint: disable=unused-argument
@responses.activate
def test_successful_token_refresh(oidc_settings): # pylint: disable=unused-argument
"""Test that the middleware successfully refreshes the token."""
@pytest.mark.parametrize("http_method", ["get", "post", "put", "patch", "delete", "head", "options"])
def test_successful_token_refresh(oidc_settings, http_method): # pylint: disable=unused-argument
"""Test that the middleware successfully refreshes the token for any HTTP method."""
user = factories.UserFactory()
request = RequestFactory().get("/test")
request = getattr(RequestFactory(), http_method)("/test")
request.user = user
get_response = MagicMock()
@@ -166,10 +167,14 @@ def test_non_expired_token(oidc_settings): # pylint: disable=unused-argument
@responses.activate
def test_refresh_token_request_timeout(oidc_settings): # pylint: disable=unused-argument
"""Test that the middleware returns a 401 response when the token refresh request times out."""
@pytest.mark.parametrize("http_method", ["get", "post", "put", "patch", "delete", "head", "options"])
def test_refresh_token_request_timeout(oidc_settings, http_method): # pylint: disable=unused-argument
"""
Test that the middleware returns a 401 response when the token
refresh request times out for any HTTP method.
"""
user = factories.UserFactory()
request = RequestFactory().get("/test")
request = getattr(RequestFactory(), http_method)("/test")
request.user = user
get_response = MagicMock()