Skip to content

Commit 589346a

Browse files
committed
✨(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: mozilla/mozilla-django-oidc#377
1 parent ea8b8be commit 589346a

File tree

2 files changed

+93
-2
lines changed

2 files changed

+93
-2
lines changed

src/backend/core/authentication/backends.py

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@
1717
logger = logging.getLogger(__name__)
1818

1919

20+
def store_oidc_refresh_token(session, refresh_token):
21+
"""Store the OIDC refresh token in the session if enabled in settings."""
22+
if import_from_settings("OIDC_STORE_REFRESH_TOKEN", False):
23+
session["oidc_refresh_token"] = refresh_token
24+
25+
2026
def store_tokens(session, access_token, id_token, refresh_token):
2127
"""Store tokens in the session if enabled in settings."""
2228
if import_from_settings("OIDC_STORE_ACCESS_TOKEN", False):
@@ -25,8 +31,7 @@ def store_tokens(session, access_token, id_token, refresh_token):
2531
if import_from_settings("OIDC_STORE_ID_TOKEN", False):
2632
session["oidc_id_token"] = id_token
2733

28-
if import_from_settings("OIDC_STORE_REFRESH_TOKEN", False):
29-
session["oidc_refresh_token"] = refresh_token
34+
store_oidc_refresh_token(session, refresh_token)
3035

3136

3237
class OIDCAuthenticationBackend(MozillaOIDCAuthenticationBackend):
@@ -36,6 +41,40 @@ class OIDCAuthenticationBackend(MozillaOIDCAuthenticationBackend):
3641
in the User and Identity models, and handles signed and/or encrypted UserInfo response.
3742
"""
3843

44+
def __init__(self, *args, **kwargs):
45+
"""
46+
Initialize the OIDC Authentication Backend.
47+
48+
Adds an internal attribute to store the token_info dictionary.
49+
The purpose of `self._token_info` is to not duplicate code from
50+
the original `authenticate` method.
51+
This won't be needed after https://github.com/mozilla/mozilla-django-oidc/pull/377
52+
is merged.
53+
"""
54+
super().__init__(*args, **kwargs)
55+
self._token_info = None
56+
57+
def get_token(self, payload):
58+
"""
59+
Return token object as a dictionary.
60+
61+
Store the value to extract the refresh token in the `authenticate` method.
62+
"""
63+
self._token_info = super().get_token(payload)
64+
return self._token_info
65+
66+
def authenticate(self, request, **kwargs):
67+
"""Authenticates a user based on the OIDC code flow."""
68+
user = super().authenticate(request, **kwargs)
69+
70+
if user is not None:
71+
# Then the user successfully authenticated
72+
store_oidc_refresh_token(
73+
request.session, self._token_info.get("refresh_token")
74+
)
75+
76+
return user
77+
3978
def get_userinfo(self, access_token, id_token, payload):
4079
"""Return user details dictionary.
4180

src/backend/core/tests/authentication/test_backends.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -547,3 +547,55 @@ def get_userinfo_mocked(*args):
547547
assert user.full_name == "Doe"
548548
assert user.short_name is None
549549
assert user.email == "[email protected]"
550+
551+
552+
@responses.activate
553+
def test_authentication_session_tokens(
554+
django_assert_num_queries, monkeypatch, rf, settings
555+
):
556+
"""
557+
Test that the session contains oidc_refresh_token and oidc_access_token after authentication.
558+
"""
559+
settings.OIDC_OP_TOKEN_ENDPOINT = "http://oidc.endpoint.test/token"
560+
settings.OIDC_OP_USER_ENDPOINT = "http://oidc.endpoint.test/userinfo"
561+
settings.OIDC_OP_JWKS_ENDPOINT = "http://oidc.endpoint.test/jwks"
562+
settings.OIDC_STORE_ACCESS_TOKEN = True
563+
settings.OIDC_STORE_REFRESH_TOKEN = True
564+
565+
klass = OIDCAuthenticationBackend()
566+
request = rf.get("/some-url", {"state": "test-state", "code": "test-code"})
567+
request.session = {}
568+
569+
def verify_token_mocked(*args, **kwargs):
570+
return {"sub": "123", "email": "[email protected]"}
571+
572+
monkeypatch.setattr(OIDCAuthenticationBackend, "verify_token", verify_token_mocked)
573+
574+
responses.add(
575+
responses.POST,
576+
re.compile(settings.OIDC_OP_TOKEN_ENDPOINT),
577+
json={
578+
"access_token": "test-access-token",
579+
"refresh_token": "test-refresh-token",
580+
},
581+
status=200,
582+
)
583+
584+
responses.add(
585+
responses.GET,
586+
re.compile(settings.OIDC_OP_USER_ENDPOINT),
587+
json={"sub": "123", "email": "[email protected]"},
588+
status=200,
589+
)
590+
591+
with django_assert_num_queries(6):
592+
user = klass.authenticate(
593+
request,
594+
code="test-code",
595+
nonce="test-nonce",
596+
code_verifier="test-code-verifier",
597+
)
598+
599+
assert user is not None
600+
assert request.session["oidc_access_token"] == "test-access-token"
601+
assert request.session["oidc_refresh_token"] == "test-refresh-token"

0 commit comments

Comments
 (0)