Skip to content

Commit 1b3fc51

Browse files
Natureshadown2ygk
authored andcommitted
OpenID: Add get_discovery_claims
This splits get_additional_claims into two forms. See documentation change for rationale.
1 parent 29d61cb commit 1b3fc51

File tree

4 files changed

+53
-14
lines changed

4 files changed

+53
-14
lines changed

docs/oidc.rst

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -245,23 +245,46 @@ required claims, eg ``iss``, ``aud``, ``exp``, ``iat``, ``auth_time`` etc),
245245
and the ``sub`` claim will use the primary key of the user as the value.
246246
You'll probably want to customize this and add additional claims or change
247247
what is sent for the ``sub`` claim. To do so, you will need to add a method to
248-
our custom validator. It should return a dictionary mapping a claim name to
249-
either the claim data, or a callable that will be called with the request to
250-
produce the claim data.
251-
Standard claim ``sub`` is included by default, to remove it override ``get_claim_list``::
248+
our custom validator. It takes one of two forms:
249+
250+
The first form gets passed a request object, and should return a dictionary
251+
mapping a claim name to claim data::
252252
class CustomOAuth2Validator(OAuth2Validator):
253253
def get_additional_claims(self, request):
254+
claims = {}
255+
claims["email"] = request.user.get_user_email()
256+
claims["username"] = request.user.get_full_name()
257+
258+
return claims
259+
260+
The second form gets no request object, and should return a dictionary
261+
mapping a claim name to a callable, accepting a request and producing
262+
the claim data::
263+
class CustomOAuth2Validator(OAuth2Validator):
264+
def get_additional_claims(self):
254265
def get_user_email(request):
255266
return request.user.get_user_email()
256267

257268
claims = {}
258-
# Element name, callback to obtain data
259269
claims["email"] = get_user_email
260-
# Element name, plain data returned
261-
claims["username"] = request.user.get_full_name()
270+
claims["username"] = lambda r: r.user.get_full_name()
262271

263272
return claims
264273

274+
Standard claim ``sub`` is included by default, to remove it override ``get_claim_dict``.
275+
276+
In some cases, it might be desirable to not list all claims in discovery info. To customize
277+
which claims are advertised, you can override the ``get_discovery_claims`` method to return
278+
a list of claim names to advertise. If your ``get_additional_claims`` uses the first form
279+
and you still want to advertise claims, you can also override ``get_discovery_claims``.
280+
281+
In order to help lcients discover claims early, they can be advertised in the discovery
282+
info, under the ``claims_supported`` key. In order for the discovery info view to automatically
283+
add all claims your validator returns, you need to use the second form (producing callables),
284+
because the discovery info views are requested with an unauthenticated request, so directly
285+
producing claim data would fail. If you use the first form, producing claim data directly,
286+
your claims will not be added to discovery info.
287+
265288
.. note::
266289
This ``request`` object is not a ``django.http.Request`` object, but an
267290
``oauthlib.common.Request`` object. This has a number of attributes that

oauth2_provider/oauth2_validators.py

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import base64
22
import binascii
33
import http.client
4+
import inspect
45
import json
56
import logging
67
import uuid
@@ -725,21 +726,34 @@ def _save_id_token(self, jti, request, expires, *args, **kwargs):
725726
)
726727
return id_token
727728

729+
@classmethod
730+
def _get_additional_claims_is_request_agnostic(cls):
731+
return len(inspect.signature(cls.get_additional_claims).parameters) == 1
732+
728733
def get_jwt_bearer_token(self, token, token_handler, request):
729734
return self.get_id_token(token, token_handler, request)
730735

731736
def get_claim_dict(self, request):
732-
def get_sub_code(inner_request):
733-
return str(inner_request.user.id)
734-
735-
claims = {"sub": get_sub_code}
737+
if self._get_additional_claims_is_request_agnostic():
738+
claims = {"sub": lambda r: str(r.user.id)}
739+
else:
740+
claims = {"sub": str(request.user.id)}
736741

737742
# https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
738-
add = self.get_additional_claims(request)
743+
if self._get_additional_claims_is_request_agnostic():
744+
add = self.get_additional_claims()
745+
else:
746+
add = self.get_additional_claims(request)
739747
claims.update(add)
740748

741749
return claims
742750

751+
def get_discovery_claims(self, request):
752+
claims = ["sub"]
753+
if self._get_additional_claims_is_request_agnostic():
754+
claims += list(self.get_claim_dict(request).keys())
755+
return claims
756+
743757
def get_oidc_claims(self, token, token_handler, request):
744758
data = self.get_claim_dict(request)
745759
claims = {}

oauth2_provider/views/oidc.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ def get(self, request, *args, **kwargs):
4848

4949
validator_class = oauth2_settings.OAUTH2_VALIDATOR_CLASS
5050
validator = validator_class()
51-
oidc_claims = list(validator.get_claim_dict(request).keys())
51+
oidc_claims = validator.get_discovery_claims(request)
52+
if "sub" not in oidc_claims:
53+
oidc_claims.append("sub")
5254

5355
data = {
5456
"issuer": issuer_url,

tests/test_oidc_views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ def claim_user_email(request):
158158
@pytest.mark.django_db
159159
def test_userinfo_endpoint_custom_claims_callable(oidc_tokens, client, oauth2_settings):
160160
class CustomValidator(OAuth2Validator):
161-
def get_additional_claims(self, request):
161+
def get_additional_claims(self):
162162
return {
163163
"username": claim_user_email,
164164
"email": claim_user_email,

0 commit comments

Comments
 (0)