Skip to content

Commit b1cb7f6

Browse files
author
Naman Shenoy
committed
OIDC_CLIENT_ALG_KEYS_HOOK specification
This update allows users of the plugin to set up custom retrieval of RSA keys used to encode/decode ID tokens.
1 parent 03d41fa commit b1cb7f6

File tree

4 files changed

+109
-15
lines changed

4 files changed

+109
-15
lines changed

docs/sections/settings.rst

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,4 +244,63 @@ A flag which toggles whether the scope is returned with successful response on i
244244

245245
Must be ``True`` to include ``scope`` into the successful response
246246

247-
Default is ``False``.
247+
Default is ``False``.
248+
249+
OIDC_CLIENT_ALG_KEYS_HOOK
250+
=========================
251+
252+
OPTIONAL. ``str``
253+
254+
A string with the location of your function hook.
255+
Here you can customize the retrieval of the RSA keys for the ID token encoding and decoding.
256+
257+
.. note::
258+
To ensure that the RSA keys provided by the ``/jwks`` endpoint is consistent with the
259+
values retrieved from this hook function, define the ``OIDC_JWKS_RESPONSE_HOOK`` response appropriately.
260+
261+
The hook function receives following arguments:
262+
263+
* ``client``: Instance of the client.
264+
265+
The hook function should return a list of `jwkest.jwk.RSAKey` of the available keys.
266+
267+
Default is::
268+
269+
def default_get_client_alg_keys(client):
270+
"""
271+
Takes a client and returns the set of keys associated with it.
272+
Returns a list of keys.
273+
"""
274+
if client.jwt_alg == 'RS256':
275+
keys = []
276+
for rsakey in RSAKey.objects.all():
277+
keys.append(jwk_RSAKey(key=importKey(rsakey.key), kid=rsakey.kid))
278+
if not keys:
279+
raise Exception('You must add at least one RSA Key.')
280+
elif client.jwt_alg == 'HS256':
281+
keys = [SYMKey(key=client.client_secret, alg=client.jwt_alg)]
282+
else:
283+
raise Exception('Unsupported key algorithm.')
284+
285+
return keys
286+
287+
288+
OIDC_JWKS_RESPONSE_HOOK
289+
=======================
290+
291+
OPTIONAL. ``str``
292+
293+
A string with the location of your function hook.
294+
Here you can provide a customized list of JWKS that will be returned by the ``/jwks`` endpoint.
295+
296+
.. note::
297+
To ensure that the RSA keys provided by the ``/jwks`` endpoint is consistent with the
298+
values retrieved from this hook function, define the ``OIDC_CLIENT_ALG_KEYS_HOOK`` appropriately.
299+
300+
This hook function takes in no arguments, and should returns a list of dictionaries with the following keys:
301+
* 'kty' with the value 'RSA'
302+
* 'alg' with the value 'RS256'
303+
* 'use' with the value 'sig'
304+
* 'kid' with the kid for the key
305+
* 'n' with the base64 representation of the modulus parameter
306+
* 'e' with the base64 representation of the exponent parameter

oidc_provider/lib/utils/token.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
from jwkest.jwk import SYMKey
99
from jwkest.jws import JWS
1010
from jwkest.jwt import JWT
11+
from jwkest import long_to_base64
1112

1213
from oidc_provider.lib.utils.common import get_issuer, run_processing_hook
1314
from oidc_provider.lib.claims import StandardScopeClaims
@@ -147,8 +148,16 @@ def create_code(user, client, scope, nonce, is_authentication,
147148

148149
return code
149150

150-
151151
def get_client_alg_keys(client):
152+
"""
153+
Hook to customize RSA Key retrieval.
154+
:param client:
155+
:return:
156+
"""
157+
client_alg_keys_hook = settings.get('OIDC_CLIENT_ALG_KEYS_HOOK')
158+
return settings.import_from_str(client_alg_keys_hook)(client)
159+
160+
def default_get_client_alg_keys(client):
152161
"""
153162
Takes a client and returns the set of keys associated with it.
154163
Returns a list of keys.
@@ -165,3 +174,22 @@ def get_client_alg_keys(client):
165174
raise Exception('Unsupported key algorithm.')
166175

167176
return keys
177+
178+
def default_get_jwks():
179+
"""
180+
Returns a list of dictionaries containing the JWKs for return by the ``jwks`` endpoint
181+
"""
182+
dic = dict(keys=[])
183+
184+
for rsakey in RSAKey.objects.all():
185+
public_key = importKey(rsakey.key).publickey()
186+
dic['keys'].append({
187+
'kty': 'RSA',
188+
'alg': 'RS256',
189+
'use': 'sig',
190+
'kid': rsakey.kid,
191+
'n': long_to_base64(public_key.n),
192+
'e': long_to_base64(public_key.e),
193+
})
194+
195+
return dic

oidc_provider/settings.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,24 @@ def OIDC_IDTOKEN_PROCESSING_HOOK(self):
129129
"""
130130
return 'oidc_provider.lib.utils.common.default_idtoken_processing_hook'
131131

132+
@property
133+
def OIDC_CLIENT_ALG_KEYS_HOOK(self):
134+
"""
135+
OPTIONAL. A string with the location of your hook.
136+
Used to specify which keys to return for a particular client and algorithm.
137+
Returns jwkest.jwk.SYMKey
138+
"""
139+
return 'oidc_provider.lib.utils.token.default_get_client_alg_keys'
140+
141+
@property
142+
def OIDC_JWKS_RESPONSE_HOOK(self):
143+
"""
144+
OPTIONAL. A string with the location of your hook.
145+
Used to specify the list of JWKS for your app.
146+
Returns a list of dictionaries that will form the response of the ``jwks`` endpoint.
147+
"""
148+
return 'oidc_provider.lib.utils.token.default_get_jwks'
149+
132150
@property
133151
def OIDC_INTROSPECTION_PROCESSING_HOOK(self):
134152
"""

oidc_provider/views.py

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -296,19 +296,8 @@ def get(self, request, *args, **kwargs):
296296

297297
class JwksView(View):
298298
def get(self, request, *args, **kwargs):
299-
dic = dict(keys=[])
300-
301-
for rsakey in RSAKey.objects.all():
302-
public_key = RSA.importKey(rsakey.key).publickey()
303-
dic['keys'].append({
304-
'kty': 'RSA',
305-
'alg': 'RS256',
306-
'use': 'sig',
307-
'kid': rsakey.kid,
308-
'n': long_to_base64(public_key.n),
309-
'e': long_to_base64(public_key.e),
310-
})
311-
299+
jwks_hook = settings.get('OIDC_JWKS_RESPONSE_HOOK')
300+
dic = settings.import_from_str(jwks_hook)()
312301
response = JsonResponse(dic)
313302
response['Access-Control-Allow-Origin'] = '*'
314303

0 commit comments

Comments
 (0)