add ability to manage OIDCResponseType

In the certains situation customers using tools like Authentik or
Zitadel may encounter issue with using id_token as in that case it may
also return access_token despite not set deliberately
The `code` flow is now the preffered approach for OIDC hence
the ability for setting that options gives more flexibility.

Implements: oidc-response-type option
Closes-Bug: #2084184
Change-Id: I251dffbdf97998998066d5efd2d8d9386ecd19e5
This commit is contained in:
Bartosz Woronicz 2024-10-10 20:36:37 +02:00
parent b70fa7d8a3
commit 7e375d1fb8
4 changed files with 49 additions and 1 deletions

View File

@ -94,6 +94,14 @@ options:
'client-cookie:persistent:store_id_token'. When using multiple units 'client-cookie:persistent:store_id_token'. When using multiple units
of Keystone behind a proxy, use 'client-cookie:persistent' if you are of Keystone behind a proxy, use 'client-cookie:persistent' if you are
not using shared session storage for Keystone. not using shared session storage for Keystone.
oidc-response-type:
default: 'id_token'
type: string
description: |
Define the OIDCResponseType for mod_auth_openidc uses limit
the responses type. It must be one of the following:
code|id_token|id_token token|code id_token|code token|code id_token token
Empty string will remove that option completely.
auth-type: auth-type:
default: 'auth-openidc' default: 'auth-openidc'
type: string type: string

View File

@ -187,6 +187,15 @@ class KeystoneOpenIDCCharm(ops_openstack.core.OSBaseCharm):
CONFIG_FILE_OWNER = 'root' CONFIG_FILE_OWNER = 'root'
CONFIG_FILE_GROUP = 'www-data' CONFIG_FILE_GROUP = 'www-data'
OIDCRESPONSETYPE_VALUES = (
'code',
'id_token',
'id_token token',
'code id_token',
'code token',
'code id_token token'
)
release = 'xena' # First release supported. release = 'xena' # First release supported.
auth_method = 'openid' # the driver to be used. auth_method = 'openid' # the driver to be used.
@ -282,6 +291,24 @@ class KeystoneOpenIDCCharm(ops_openstack.core.OSBaseCharm):
data['idp-name'] = json.dumps(self.options.idp_id) data['idp-name'] = json.dumps(self.options.idp_id)
data['user-facing-name'] = json.dumps(self.options.user_facing_name) data['user-facing-name'] = json.dumps(self.options.user_facing_name)
def _check_oidcresponsetype(self):
"""Make sure that the OIDCResponseType got correct value
As in description the allowed values are:
code|id_token|id_token token|code id_token|
code token|code id_token token
Otherwise raise CharmConfigError
"""
options = KeystoneOpenIDCOptions(self)
if options.oidc_response_type not in self.OIDCRESPONSETYPE_VALUES:
valid_values = '|'.join(self.OIDCRESPONSETYPE_VALUES)
msg = (
f"Incorrect oidc-response-type:{options.oidc_response_type}.",
f"Should be one of the following: {valid_values}."
)
logger.error(msg)
raise CharmConfigError(msg)
def _on_config_changed(self, event): def _on_config_changed(self, event):
if not self.is_data_ready(): if not self.is_data_ready():
logger.debug('relation data is not ready yet (%s)', event) logger.debug('relation data is not ready yet (%s)', event)
@ -294,6 +321,7 @@ class KeystoneOpenIDCCharm(ops_openstack.core.OSBaseCharm):
self.update_config_if_needed() self.update_config_if_needed()
self.update_principal_data() self.update_principal_data()
self._update_websso_data() self._update_websso_data()
self._check_oidcresponsetype()
def update_config_if_needed(self): def update_config_if_needed(self):
with ch_host.restart_on_change( with ch_host.restart_on_change(

View File

@ -1,6 +1,8 @@
{# -*- mode: apache -*- #} {# -*- mode: apache -*- #}
OIDCClaimPrefix "OIDC-" OIDCClaimPrefix "OIDC-"
OIDCResponseType "id_token" {% if options.oidc_response_type -%}
OIDCResponseType {{ options.oidc_response_type }}
{% endif -%}
OIDCScope "openid email profile" OIDCScope "openid email profile"
{% if options.oidc_session_type -%} {% if options.oidc_session_type -%}

View File

@ -203,3 +203,13 @@ class TestCharm(BaseTestCharm):
data = self.harness.get_relation_data(rid, 'keystone-openidc/0') data = self.harness.get_relation_data(rid, 'keystone-openidc/0')
expected['user-facing-name'] = json.dumps('My IdP') expected['user-facing-name'] = json.dumps('My IdP')
self.assertDictEqual(data, expected) self.assertDictEqual(data, expected)
def test_check_oidcresponsetype(self):
opts = {
'oidc-response-type': 'invalid_option'}
self.harness.update_config(
key_values=opts)
self.assertRaises(
charm.CharmConfigError,
self.harness.charm._check_oidcresponsetype
)