1
1
import logging
2
2
3
3
import jwt
4
+ from django .conf import settings
4
5
from django .core .exceptions import ValidationError
5
6
from django .core .validators import MinValueValidator
6
7
from django .utils .translation import gettext_lazy as _
@@ -123,7 +124,11 @@ class OpenIdConnectConfiguration(BaseAuthenticatorConfiguration):
123
124
)
124
125
125
126
JWT_ALGORITHMS = ListField (
126
- help_text = _ ("The algorithm(s) for decoding JWT responses from the IDP." ),
127
+ help_text = _ (
128
+ "The algorithm(s) for decoding JWT responses from the IDP. "
129
+ "Leave blank to extract from the .well-known configuration (if that fails we will attempt the default algorithms). "
130
+ "Set to ['none'] to not use encrypted tokens (the provider must send unencrypted tokens for this to work)"
131
+ ),
127
132
default = None ,
128
133
allow_null = True ,
129
134
validators = [JWTAlgorithmListFieldValidator ()],
@@ -263,6 +268,29 @@ def public_key(self):
263
268
)
264
269
return None
265
270
271
+ def _get_jwt_algorithms (self ) -> list [str ]:
272
+ """
273
+ Get the JWT algorithms to pass to the decode
274
+ """
275
+ if self .setting ("JWT_ALGORITHMS" ):
276
+ # If the admin specified the algorithms then use them
277
+ return self .setting ("JWT_ALGORITHMS" )
278
+ else :
279
+ # Try to get the algorithms from the .well-known/openid-configuration
280
+ try :
281
+ logger .debug ("Attempting to get the JWT algorithms from the .well-known/openid-configuration" )
282
+ config = self .oidc_config ()
283
+ idp_algorithms = config .get ("id_token_encryption_alg_values_supported" )
284
+ if idp_algorithms :
285
+ logger .debug (f"JWT algorithms supported by the IDP: { idp_algorithms } " )
286
+ return idp_algorithms
287
+ raise Exception ("No algorithms found in OIDC config" )
288
+ except Exception as e :
289
+ # In controller 2.4 we did not have the ability to set the JWT algorithms so we will use the default algorithms
290
+ lib_defaults = getattr (settings , 'JWT_ALGORITHMS' , super ().JWT_ALGORITHMS )
291
+ logger .error (f"Unable to get JWT algorithms from the .well-known/openid-configuration, defaulting to { lib_defaults } : { e } " )
292
+ return lib_defaults
293
+
266
294
def user_data (self , access_token , * args , ** kwargs ):
267
295
"""
268
296
This function overrides the one in social auth class OpenIdConnectAuth, since
@@ -277,18 +305,25 @@ def user_data(self, access_token, *args, **kwargs):
277
305
# If the content type is application/jwt than we can assume that the token is encrypted. Otherwise it should be application/json
278
306
pubkey = self .public_key ()
279
307
if not pubkey :
280
- logger .error (_ ( "OIDC client sent encrypted user info response, but no public key found." ) )
308
+ logger .error ("OIDC client sent encrypted user info response, but no public key found." )
281
309
return None
310
+ # Get the algorithms to pass to the decode
311
+ algorithms = self ._get_jwt_algorithms ()
312
+ options = {}
313
+ if algorithms == ['none' ]:
314
+ logger .info ("JWT decryption algorithm is set to ['none'], will proceed but this is insecure" )
315
+ options ['verify_signature' ] = False
282
316
try :
283
317
data = jwt .decode (
284
318
access_token ,
285
319
key = pubkey ,
286
- algorithms = self . setting ( "JWT_ALGORITHMS" ) ,
320
+ algorithms = algorithms ,
287
321
audience = self .setting ("KEY" ),
322
+ options = options ,
288
323
)
289
324
return data
290
325
except PyJWTError as e :
291
- logger .error (_ ( f"Unable to decode user info response JWT: { e } " ) )
326
+ logger .error (f"Unable to decode user info response JWT: { e } " )
292
327
return None
293
328
return user_data .json ()
294
329
0 commit comments