Skip to content

Commit be7f0fa

Browse files
committed
Update description of OIDC scopes for claims and clarify related info.
1 parent e78010e commit be7f0fa

File tree

1 file changed

+66
-26
lines changed

1 file changed

+66
-26
lines changed

docs/oidc.rst

Lines changed: 66 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ so there is no need to add a setting for the public key.
102102

103103

104104
Rotating the RSA private key
105-
~~~~~~~~~~~~~~~~~~~~~~~~
105+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
106106
Extra keys can be published in the jwks_uri with the ``OIDC_RSA_PRIVATE_KEYS_INACTIVE``
107107
setting. For example:::
108108

@@ -250,33 +250,40 @@ our custom validator. It takes one of two forms:
250250
The first form gets passed a request object, and should return a dictionary
251251
mapping a claim name to claim data::
252252
class CustomOAuth2Validator(OAuth2Validator):
253+
# Set `oidc_claim_scope = None` to ignore scopes that limit which claims to return,
254+
# otherwise the OIDC standard scopes are used.
255+
253256
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+
return {
258+
"given_name": request.user.first_name,
259+
"family_name": request.user.last_name,
260+
"name": ' '.join([request.user.first_name, request.user.last_name]),
261+
"preferred_username": request.user.username,
262+
"email": request.user.email,
263+
}
257264

258-
return claims
259265

260266
The second form gets no request object, and should return a dictionary
261267
mapping a claim name to a callable, accepting a request and producing
262268
the claim data::
263269
class CustomOAuth2Validator(OAuth2Validator):
264-
def get_additional_claims(self):
265-
def get_user_email(request):
266-
return request.user.get_user_email()
270+
# Set `oidc_claim_scope = None` to ignore scopes that limit which claims to return,
271+
# otherwise the OIDC standard scopes are used.
267272

268-
claims = {}
269-
claims["email"] = get_user_email
270-
claims["username"] = lambda r: r.user.get_full_name()
273+
def get_additional_claims(self):
274+
return {
275+
"given_name": lambda request: request.user.first_name,
276+
"family_name": lambda request: request.user.last_name,
277+
"name": lambda request: ' '.join([request.user.first_name, request.user.last_name]),
278+
"preferred_username": lambda request: request.user.username,
279+
"email": lambda request: request.user.email,
280+
}
271281

272-
return claims
273282

274283
Standard claim ``sub`` is included by default, to remove it override ``get_claim_dict``.
275284

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``.
285+
Supported claims discovery
286+
--------------------------
280287

281288
In order to help clients discover claims early, they can be advertised in the discovery
282289
info, under the ``claims_supported`` key. In order for the discovery info view to automatically
@@ -285,19 +292,52 @@ because the discovery info views are requested with an unauthenticated request,
285292
producing claim data would fail. If you use the first form, producing claim data directly,
286293
your claims will not be added to discovery info.
287294

295+
In some cases, it might be desirable to not list all claims in discovery info. To customize
296+
which claims are advertised, you can override the ``get_discovery_claims`` method to return
297+
a list of claim names to advertise. If your ``get_additional_claims`` uses the first form
298+
and you still want to advertise claims, you can also override ``get_discovery_claims``.
299+
300+
Using OIDC scopes to determine which claims are returned
301+
--------------------------------------------------------
302+
303+
The ``oidc_claim_scope`` OAuth2Validator class attribute implements OIDC's
304+
`5.4 Requesting Claims using Scope Values`_ feature.
305+
For example, a ``given_name`` claim is only returned if the ``profile`` scope was granted.
306+
307+
To change the list of claims and which scopes result in their being returned,
308+
override ``oidc_claim_scope`` with a dict keyed by claim with a value of scope.
309+
The following example adds instructions to return the ``foo`` claim when the ``bar`` scope is granted::
310+
class CustomOAuth2Validator(OAuth2Validator):
311+
oidc_claim_scope = OAuth2Validator.oidc_claim_scope
312+
oidc_claim_scope.update({"foo": "bar"})
313+
314+
Set ``oidc_claim_scope = None`` to return all claims irrespective of the granted scopes.
315+
316+
You have to make sure you've added addtional claims via ``get_aditional_claims``
317+
and defined the ``OAUTH2_PROVIDER["SCOPES"]`` in your settings in order for this functionality to work.
318+
319+
320+
Using the OIDC "claims" request parameter to determine which claims are returned
321+
--------------------------------------------------------------------------------
322+
323+
Besides using standard OIDC scopes, you can use OIDC's
324+
`5.5 Requesting Claims using the "claims" Request Parameter`_ feature.
325+
326+
TODO - confirm that this works and document so and whether it interferes with the scope stuff.
327+
328+
288329
.. note::
289330
This ``request`` object is not a ``django.http.Request`` object, but an
290331
``oauthlib.common.Request`` object. This has a number of attributes that
291332
you can use to decide what claims to put in to the ID token:
292333

293-
* ``request.scopes`` - a list of the scopes requested by the client when
294-
making an authorization request.
295-
* ``request.claims`` - a dictionary of the requested claims, using the
296-
`OIDC claims requesting system`_. These must be requested by the client
297-
when making an authorization request.
334+
* ``request.scopes`` - is `NOT the list of granted scopes <https://github.com/oauthlib/oauthlib/issues/799>`_. For that, use:
335+
* ``request.access_token.scopes`` - the list of granted scopes.
336+
* ``request.claims`` - a dictionary of the claims.
298337
* ``request.user`` - the django user object.
299338

300-
.. _OIDC claims requesting system: https://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter
339+
.. _5.4 Requesting Claims using Scope Values: https://openid.net/specs/openid-connect-core-1_0.html#ScopeClaims
340+
.. _5.5 Requesting Claims using the "claims" Request Parameter: https://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter
301341

302342
What claims you decide to put in to the token is up to you to determine based
303343
upon what the scopes and / or claims means to your provider.
@@ -307,11 +347,11 @@ Adding information to the ``UserInfo`` service
307347
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
308348

309349
The ``UserInfo`` service is supplied as part of the OIDC service, and is used
310-
to retrieve more information about the user than was supplied in the ID token
311-
when the user logged in to the OIDC client application. It is optional to use
312-
the service. The service is accessed by making a request to the
350+
to retrieve information about the user given their Access Token.
351+
It is optional to use the service. The service is accessed by making a request to the
313352
``UserInfo`` endpoint, eg ``/o/userinfo/`` and supplying the access token
314-
retrieved at login as a ``Bearer`` token.
353+
retrieved at login as a ``Bearer`` token or as a form-encoded ``access_token`` body parameter
354+
for a POST request.
315355

316356
Again, to modify the content delivered, we need to add a function to our
317357
custom validator. The default implementation adds the claims from the ID

0 commit comments

Comments
 (0)