Skip to content

Commit 6728570

Browse files
committed
Add Default JWT Algorithms from .well-known endpoint
1 parent 56bfaf4 commit 6728570

File tree

4 files changed

+672
-4
lines changed

4 files changed

+672
-4
lines changed

ansible_base/authentication/authenticator_plugins/oidc.py

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,11 @@ class OpenIdConnectConfiguration(BaseAuthenticatorConfiguration):
123123
)
124124

125125
JWT_ALGORITHMS = ListField(
126-
help_text=_("The algorithm(s) for decoding JWT responses from the IDP."),
126+
help_text=_(
127+
"The algorithm(s) for decoding JWT responses from the IDP. "
128+
"Leave blank to extract from the .well-known configuration (if that fails we will attempt the default algorithms). "
129+
"Set to ['none'] to not use encrypted tokens (the provider must send unencrypted tokens for this to work)"
130+
),
127131
default=None,
128132
allow_null=True,
129133
validators=[JWTAlgorithmListFieldValidator()],
@@ -300,3 +304,56 @@ def get_alternative_uid(self, **kwargs):
300304
return preferred_username
301305

302306
return None
307+
308+
def _discover_algorithms_from_config(self, config):
309+
"""
310+
Discover JWT algorithms from OIDC configuration
311+
"""
312+
# Try signing algorithms first (most common)
313+
algorithms = config.get("id_token_signing_alg_values_supported")
314+
if algorithms:
315+
logger.debug(f"JWT signing algorithms supported by the IDP: {algorithms}")
316+
return algorithms
317+
318+
# Fallback to encryption algorithms
319+
algorithms = config.get("id_token_encryption_alg_values_supported")
320+
if algorithms:
321+
logger.debug(f"JWT encryption algorithms supported by the IDP: {algorithms}")
322+
return algorithms
323+
324+
# Try userinfo signing algorithms as final fallback
325+
algorithms = config.get("userinfo_signing_alg_values_supported")
326+
if algorithms:
327+
logger.debug(f"JWT userinfo signing algorithms supported by the IDP: {algorithms}")
328+
return algorithms
329+
330+
return None
331+
332+
def _get_jwt_algorithms(self, existing_setting=None) -> list[str]:
333+
"""
334+
Get the JWT algorithms to pass to the decode
335+
"""
336+
if existing_setting:
337+
algorithms = existing_setting
338+
else:
339+
try:
340+
logger.debug("Attempting to get the JWT algorithms from the .well-known/openid-configuration")
341+
config = self.oidc_config()
342+
algorithms = self._discover_algorithms_from_config(config)
343+
344+
if not algorithms:
345+
raise Exception("No algorithms found in OIDC config")
346+
347+
except Exception as e:
348+
# Fallback to default algorithms if discovery fails
349+
lib_defaults = OpenIdConnectAuth.JWT_ALGORITHMS
350+
logger.error(f"Unable to get JWT algorithms from the .well-known/openid-configuration, defaulting to {lib_defaults}: {e}")
351+
algorithms = lib_defaults
352+
353+
# Ensure we always return a list
354+
if not isinstance(algorithms, list):
355+
algorithms = [algorithms] if algorithms else []
356+
357+
# Set the property for use elsewhere
358+
self.JWT_ALGORITHMS = algorithms
359+
return algorithms

ansible_base/authentication/models/authenticator.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,31 @@ class Authenticator(UniqueNamedCommonModel):
5252
on_delete=SET_NULL,
5353
)
5454

55+
def save_default_jwt_algorithms(self):
56+
try:
57+
# Create a proper plugin instance with the database instance
58+
from ansible_base.authentication.authenticator_plugins.utils import get_authenticator_class
59+
60+
authenticator_class = get_authenticator_class(self.type)
61+
plugin_instance = authenticator_class(database_instance=self)
62+
63+
# Try to get algorithms from .well-known endpoint
64+
import logging
65+
66+
logger = logging.getLogger('ansible_base.authentication.models.authenticator')
67+
logger.info("Auto-populating JWT algorithms for OIDC authenticator from .well-known endpoint")
68+
69+
algorithms = plugin_instance._get_jwt_algorithms()
70+
if algorithms:
71+
self.configuration['JWT_ALGORITHMS'] = algorithms
72+
logger.info(f"Successfully populated JWT algorithms: {algorithms}")
73+
except Exception as e:
74+
import logging
75+
76+
logger = logging.getLogger('ansible_base.authentication.models.authenticator')
77+
logger.warning(f"Could not auto-populate JWT algorithms for OIDC authenticator: {e}")
78+
# Don't fail the save operation, just log the warning
79+
5580
def save(self, *args, **kwargs):
5681
from ansible_base.lib.utils.encryption import ansible_encryption
5782

@@ -65,6 +90,10 @@ def save(self, *args, **kwargs):
6590
if field in self.configuration:
6691
self.configuration[field] = ansible_encryption.encrypt_string(self.configuration[field])
6792

93+
# Auto-populate JWT algorithms for OIDC authenticators if not set
94+
if self.type == "ansible_base.authentication.authenticator_plugins.oidc" and self.configuration and not self.configuration.get('JWT_ALGORITHMS'):
95+
self.save_default_jwt_algorithms()
96+
6897
if not self.slug:
6998
self.slug = generate_authenticator_slug()
7099
super().save(*args, **kwargs)

0 commit comments

Comments
 (0)