Skip to content

Commit

Permalink
Merge pull request #508 from akatsoulas/custom-username-claims
Browse files Browse the repository at this point in the history
Custom username claims
  • Loading branch information
akatsoulas authored Nov 24, 2023
2 parents 5450e40 + 6be570a commit 878ccd6
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 3 deletions.
14 changes: 11 additions & 3 deletions mozilla_django_oidc/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import json
import logging

import inspect
import requests
from django.contrib.auth import get_user_model
from django.contrib.auth.backends import ModelBackend
Expand All @@ -20,10 +21,11 @@
LOGGER = logging.getLogger(__name__)


def default_username_algo(email):
def default_username_algo(email, claims=None):
"""Generate username for the Django user.
:arg str/unicode email: the email address to use to generate a username
:arg dic claims: the claims from your OIDC provider, currently unused
:returns: str/unicode
Expand Down Expand Up @@ -100,14 +102,20 @@ def get_username(self, claims):
"""Generate username based on claims."""
# bluntly stolen from django-browserid
# https://github.com/mozilla/django-browserid/blob/master/django_browserid/auth.py

username_algo = self.get_settings("OIDC_USERNAME_ALGO", None)

if username_algo:
if isinstance(username_algo, str):
username_algo = import_string(username_algo)
return username_algo(claims.get("email"))
if len(inspect.getfullargspec(username_algo).args) == 1:
# this is for backwards compatibility only
return username_algo(claims.get("email"))
else:
# also pass the claims to the custom user name algo
return username_algo(claims.get("email"), claims)

return default_username_algo(claims.get("email"))
return default_username_algo(claims.get("email"), claims)

def update_user(self, user, claims):
"""Update existing user with new claims, if necessary save, and return user"""
Expand Down
38 changes: 38 additions & 0 deletions tests/test_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -736,6 +736,38 @@ def test_custom_username_algo_dotted_path(self, request_mock, jws_mock):
User.objects.get(username="dotted_username_algo"),
)

@override_settings(
OIDC_USE_NONCE=False,
OIDC_USERNAME_ALGO="tests.test_auth.dotted_username_algo_callback_with_claims",
)
@patch("mozilla_django_oidc.auth.OIDCAuthenticationBackend._verify_jws")
@patch("mozilla_django_oidc.auth.requests")
def test_dotted_username_algo_callback_with_claims(self, request_mock, jws_mock):
"""Test user creation with custom username algorithm with a dotted path."""
auth_request = RequestFactory().get("/foo", {"code": "foo", "state": "bar"})
auth_request.session = {}

self.assertEqual(User.objects.filter(email="[email protected]").exists(), False)
jws_mock.return_value = json.dumps({"nonce": "nonce"}).encode("utf-8")
domain = "django.con"
get_json_mock = Mock()
get_json_mock.json.return_value = {
"nickname": "a_username",
"email": "[email protected]",
"domain": domain,
}
request_mock.get.return_value = get_json_mock
post_json_mock = Mock()
post_json_mock.json.return_value = {
"id_token": "id_token",
"access_token": "access_granted",
}
request_mock.post.return_value = post_json_mock
self.assertEqual(
self.backend.authenticate(request=auth_request),
User.objects.get(username=f"{domain}/[email protected]"),
)

@override_settings(OIDC_USE_NONCE=False)
@patch("mozilla_django_oidc.auth.OIDCAuthenticationBackend._verify_jws")
@patch("mozilla_django_oidc.auth.requests")
Expand Down Expand Up @@ -1170,3 +1202,9 @@ def test_returns_true_custom_claims(self, patch_logger, patch_settings):

def dotted_username_algo_callback(email):
return "dotted_username_algo"


def dotted_username_algo_callback_with_claims(email, claims=None):
domain = claims["domain"]
username = f"{domain}/{email}"
return username

0 comments on commit 878ccd6

Please sign in to comment.