Skip to content

Commit 56e27f2

Browse files
Altering how JWT_ALGORITHMS work in oidc authenticator
1 parent 6b2efe3 commit 56e27f2

File tree

1 file changed

+38
-4
lines changed
  • ansible_base/authentication/authenticator_plugins

1 file changed

+38
-4
lines changed

ansible_base/authentication/authenticator_plugins/oidc.py

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import logging
22

33
import jwt
4+
from django.conf import settings
45
from django.core.exceptions import ValidationError
56
from django.core.validators import MinValueValidator
67
from django.utils.translation import gettext_lazy as _
@@ -123,7 +124,11 @@ class OpenIdConnectConfiguration(BaseAuthenticatorConfiguration):
123124
)
124125

125126
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+
),
127132
default=None,
128133
allow_null=True,
129134
validators=[JWTAlgorithmListFieldValidator()],
@@ -263,6 +268,28 @@ def public_key(self):
263268
)
264269
return None
265270

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+
except Exception as e:
288+
# In controller 2.4 we did not have the ability to set the JWT algorithms so we will use the default algorithms
289+
lib_defaults = settings.get('JWT_ALGORITHMS', super().JWT_ALGORITHMS)
290+
logger.error(f"Unable to get JWT algorithms from the .well-known/openid-configuration, defaulting to {lib_defaults}: {e}")
291+
return lib_defaults
292+
266293
def user_data(self, access_token, *args, **kwargs):
267294
"""
268295
This function overrides the one in social auth class OpenIdConnectAuth, since
@@ -277,18 +304,25 @@ def user_data(self, access_token, *args, **kwargs):
277304
# If the content type is application/jwt than we can assume that the token is encrypted. Otherwise it should be application/json
278305
pubkey = self.public_key()
279306
if not pubkey:
280-
logger.error(_("OIDC client sent encrypted user info response, but no public key found."))
307+
logger.error("OIDC client sent encrypted user info response, but no public key found.")
281308
return None
309+
# Get the algorithms to pass to the decode
310+
algorithms = self._get_jwt_algorithms()
311+
options = {}
312+
if algorithms == ['none']:
313+
logger.info("JWT decryption algorithm is set to ['none'], will proceed but this is insecure")
314+
options['verify_signature'] = False
282315
try:
283316
data = jwt.decode(
284317
access_token,
285318
key=pubkey,
286-
algorithms=self.setting("JWT_ALGORITHMS"),
319+
algorithms=algorithms,
287320
audience=self.setting("KEY"),
321+
options=options,
288322
)
289323
return data
290324
except PyJWTError as e:
291-
logger.error(_(f"Unable to decode user info response JWT: {e}"))
325+
logger.error(f"Unable to decode user info response JWT: {e}")
292326
return None
293327
return user_data.json()
294328

0 commit comments

Comments
 (0)