Skip to content

Commit a34be99

Browse files
Adds the ability to define how to store a user (#1328)
* Update oauth2_validators.py * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * add docs & tests * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 2ef14c5 commit a34be99

File tree

5 files changed

+33
-3
lines changed

5 files changed

+33
-3
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ Hasan Ramezani
6060
Hiroki Kiyohara
6161
Hossein Shakiba
6262
Islam Kamel
63+
Ivan Lukyanets
6364
Jadiel Teófilo
6465
Jens Timmerman
6566
Jerome Leclanche

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3232
* #1337 Gracefully handle expired or deleted refresh tokens, in `validate_user`.
3333
* #1350 Support Python 3.12 and Django 5.0
3434
* #1249 Add code_challenge_methods_supported property to auto discovery information, per [RFC 8414 section 2](https://www.rfc-editor.org/rfc/rfc8414.html#page-7)
35+
* #1328 Adds the ability to define how to store a user profile
3536

3637

3738
### Fixed

docs/oidc.rst

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,17 @@ In the docs below, it assumes that you have mounted the
404404
the URLs accordingly.
405405

406406

407+
Define where to store the profile
408+
=================================
409+
410+
.. py:function:: OAuth2Validator.get_or_create_user_from_content(content)
411+
412+
An optional layer to define where to store the profile in ``UserModel`` or a separate model. For example ``UserOAuth``, where ``user = models.OneToOneField(UserModel)``.
413+
414+
The function is called after checking that the username is present in the content.
415+
416+
:return: An instance of the ``UserModel`` representing the user fetched or created.
417+
407418
ConnectDiscoveryInfoView
408419
~~~~~~~~~~~~~~~~~~~~~~~~
409420

oauth2_provider/oauth2_validators.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,17 @@ def validate_client_id(self, client_id, request, *args, **kwargs):
333333
def get_default_redirect_uri(self, client_id, request, *args, **kwargs):
334334
return request.client.default_redirect_uri
335335

336+
def get_or_create_user_from_content(self, content):
337+
"""
338+
An optional layer to define where to store the profile in `UserModel` or a separate model. For example `UserOAuth`, where `user = models.OneToOneField(UserModel)` .
339+
340+
The function is called after checking that username is in the content.
341+
342+
Returns an UserModel instance;
343+
"""
344+
user, _ = UserModel.objects.get_or_create(**{UserModel.USERNAME_FIELD: content["username"]})
345+
return user
346+
336347
def _get_token_from_authentication_server(
337348
self, token, introspection_url, introspection_token, introspection_credentials
338349
):
@@ -383,9 +394,7 @@ def _get_token_from_authentication_server(
383394

384395
if "active" in content and content["active"] is True:
385396
if "username" in content:
386-
user, _created = UserModel.objects.get_or_create(
387-
**{UserModel.USERNAME_FIELD: content["username"]}
388-
)
397+
user = self.get_or_create_user_from_content(content)
389398
else:
390399
user = None
391400

tests/test_oauth2_validators.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,14 @@ def test_save_bearer_token__with_new_token__calls_methods_to_create_access_and_r
335335
assert create_access_token_mock.call_count == 1
336336
assert create_refresh_token_mock.call_count == 1
337337

338+
def test_get_or_create_user_from_content(self):
339+
content = {"username": "test_user"}
340+
UserModel.objects.filter(username=content["username"]).delete()
341+
user = self.validator.get_or_create_user_from_content(content)
342+
343+
self.assertIsNotNone(user)
344+
self.assertEqual(content["username"], user.username)
345+
338346

339347
class TestOAuth2ValidatorProvidesErrorData(TransactionTestCase):
340348
"""These test cases check that the recommended error codes are returned

0 commit comments

Comments
 (0)