(oidc) store refresh token in session

This code is not very clean but it reduces the footprint
to add refresh token storage in session.
This could be cleaned up once the PR it merged:
https://github.com/mozilla/mozilla-django-oidc/pull/377
This commit is contained in:
Quentin BEY
2025-03-19 17:05:36 +01:00
parent ea8b8be5f0
commit 589346acba
2 changed files with 93 additions and 2 deletions
+41 -2
View File
@@ -17,6 +17,12 @@ from core.models import DuplicateEmailError, User
logger = logging.getLogger(__name__)
def store_oidc_refresh_token(session, refresh_token):
"""Store the OIDC refresh token in the session if enabled in settings."""
if import_from_settings("OIDC_STORE_REFRESH_TOKEN", False):
session["oidc_refresh_token"] = refresh_token
def store_tokens(session, access_token, id_token, refresh_token):
"""Store tokens in the session if enabled in settings."""
if import_from_settings("OIDC_STORE_ACCESS_TOKEN", False):
@@ -25,8 +31,7 @@ def store_tokens(session, access_token, id_token, refresh_token):
if import_from_settings("OIDC_STORE_ID_TOKEN", False):
session["oidc_id_token"] = id_token
if import_from_settings("OIDC_STORE_REFRESH_TOKEN", False):
session["oidc_refresh_token"] = refresh_token
store_oidc_refresh_token(session, refresh_token)
class OIDCAuthenticationBackend(MozillaOIDCAuthenticationBackend):
@@ -36,6 +41,40 @@ class OIDCAuthenticationBackend(MozillaOIDCAuthenticationBackend):
in the User and Identity models, and handles signed and/or encrypted UserInfo response.
"""
def __init__(self, *args, **kwargs):
"""
Initialize the OIDC Authentication Backend.
Adds an internal attribute to store the token_info dictionary.
The purpose of `self._token_info` is to not duplicate code from
the original `authenticate` method.
This won't be needed after https://github.com/mozilla/mozilla-django-oidc/pull/377
is merged.
"""
super().__init__(*args, **kwargs)
self._token_info = None
def get_token(self, payload):
"""
Return token object as a dictionary.
Store the value to extract the refresh token in the `authenticate` method.
"""
self._token_info = super().get_token(payload)
return self._token_info
def authenticate(self, request, **kwargs):
"""Authenticates a user based on the OIDC code flow."""
user = super().authenticate(request, **kwargs)
if user is not None:
# Then the user successfully authenticated
store_oidc_refresh_token(
request.session, self._token_info.get("refresh_token")
)
return user
def get_userinfo(self, access_token, id_token, payload):
"""Return user details dictionary.
@@ -547,3 +547,55 @@ def test_authentication_verify_claims_success(django_assert_num_queries, monkeyp
assert user.full_name == "Doe"
assert user.short_name is None
assert user.email == "[email protected]"
@responses.activate
def test_authentication_session_tokens(
django_assert_num_queries, monkeypatch, rf, settings
):
"""
Test that the session contains oidc_refresh_token and oidc_access_token after authentication.
"""
settings.OIDC_OP_TOKEN_ENDPOINT = "http://oidc.endpoint.test/token"
settings.OIDC_OP_USER_ENDPOINT = "http://oidc.endpoint.test/userinfo"
settings.OIDC_OP_JWKS_ENDPOINT = "http://oidc.endpoint.test/jwks"
settings.OIDC_STORE_ACCESS_TOKEN = True
settings.OIDC_STORE_REFRESH_TOKEN = True
klass = OIDCAuthenticationBackend()
request = rf.get("/some-url", {"state": "test-state", "code": "test-code"})
request.session = {}
def verify_token_mocked(*args, **kwargs):
return {"sub": "123", "email": "[email protected]"}
monkeypatch.setattr(OIDCAuthenticationBackend, "verify_token", verify_token_mocked)
responses.add(
responses.POST,
re.compile(settings.OIDC_OP_TOKEN_ENDPOINT),
json={
"access_token": "test-access-token",
"refresh_token": "test-refresh-token",
},
status=200,
)
responses.add(
responses.GET,
re.compile(settings.OIDC_OP_USER_ENDPOINT),
json={"sub": "123", "email": "[email protected]"},
status=200,
)
with django_assert_num_queries(6):
user = klass.authenticate(
request,
code="test-code",
nonce="test-nonce",
code_verifier="test-code-verifier",
)
assert user is not None
assert request.session["oidc_access_token"] == "test-access-token"
assert request.session["oidc_refresh_token"] == "test-refresh-token"