Update:

Just found https://lists.openstack.org/archives/list/openstack-discuss@lists.openstack.org/thread/II2V67ZXUU26U43EOZDDQVCS3JWJUHVX/#II2V67ZXUU26U43EOZDDQVCS3JWJUHVX where Jean-Francois is discussing the solution to be:

OIDCClaimDelimiter ";"


And it seems to be working for me! 

On 13 Dec 2023, at 12:00, Franciszek Przewoźny <fprzewozny@opera.com> wrote:

Hi all,

I'm having some troubles integrating Keystone with Keycloak - pulling username and email from Keycloak using OIDC to Keystone works fine, but pulling groups (list of groups) doesn't. Keycloak provides such data in userinfo (pulled from Google, mapped in Keycloak and added to user):
  "usergroups": [
    "group-a",
    "group-b",
  ],
Keystone seems to be getting group names, but incorrectly parsing (?) it to a string with commas (instead of semicolon as it's required for multiple group entries to work: https://docs.openstack.org/keystone/latest/admin/federation/mapping_combinations.html#mappings-examples):
2023-12-13 08:22:13.399 1076680 DEBUG keystone.federation.utils [None req-e8b8519a-c639-450e-b4e7-ca2ea8a0f3bc - - - - - -] updating a direct mapping: ['group-a,group-b'] _verify_all_requirements /usr/lib/python3/dist-packages/keystone/federation/utils.py:867
And tries to map user to group with names group-a,group-b:
2023-12-13 09:42:54.914 1084357 DEBUG keystone.federation.utils [None req-df016b4f-1ab4-4e1a-bd9f-314d9434b302 - - - - - -] mapped_properties: {'user': {'name': 'xxxxxxxx', 'email': 'xxxxxxx@yyyy.zzz', 'domain': {'id': 'default'}, 'type': 'ephemeral'}, 'group_ids': [], 'group_names': [{'name': 'group-a,group-b', 'domain': {'id': 'default'}}], 'projects': []} process /usr/lib/python3/dist-packages/keystone/federation/utils.py:562
2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application [None req-df016b4f-1ab4-4e1a-bd9f-314d9434b302 - - - - - -] 'method' object is not subscriptable: TypeError: 'method' object is not subscriptable 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application Traceback (most recent call last): 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application File "/usr/lib/python3/dist-packages/flask/app.py", line 1513, in full_dispatch_request 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application rv = self.dispatch_request() 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application File "/usr/lib/python3/dist-packages/flask/app.py", line 1499, in dispatch_request 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application return self.ensure_sync(self.view_functions[rule.endpoint])(**req.view_args) 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application File "/usr/lib/python3/dist-packages/flask_restful/__init__.py", line 467, in wrapper 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application resp = resource(*args, **kwargs) 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application File "/usr/lib/python3/dist-packages/flask/views.py", line 83, in view 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application return self.dispatch_request(*args, **kwargs) 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application File "/usr/lib/python3/dist-packages/flask_restful/__init__.py", line 582, in dispatch_request 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application resp = meth(*args, **kwargs) 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application File "/usr/lib/python3/dist-packages/keystone/server/flask/common.py", line 1064, in wrapper 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application return f(*args, **kwargs) 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application File "/usr/lib/python3/dist-packages/keystone/api/auth.py", line 386, in get 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application return self._perform_auth(idp_id, protocol_id) 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application File "/usr/lib/python3/dist-packages/keystone/api/auth.py", line 380, in _perform_auth 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application token = authentication.federated_authenticate_for_token( 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application File "/usr/lib/python3/dist-packages/keystone/api/_shared/authentication.py", line 251, in federated_authenticate_for_token 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application return authenticate_for_token(auth) 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application File "/usr/lib/python3/dist-packages/keystone/api/_shared/authentication.py", line 185, in authenticate_for_token 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application authenticate(auth_info, auth_context) 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application File "/usr/lib/python3/dist-packages/keystone/api/_shared/authentication.py", line 152, in authenticate 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application resp = method.authenticate(auth_info.get_method_data(method_name)) 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application File "/usr/lib/python3/dist-packages/keystone/auth/plugins/mapped.py", line 59, in authenticate 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application response_data = handle_unscoped_token(auth_payload, 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application File "/usr/lib/python3/dist-packages/keystone/auth/plugins/mapped.py", line 225, in handle_unscoped_token 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application mapped_properties, mapping_id = apply_mapping_filter( 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application File "/usr/lib/python3/dist-packages/keystone/auth/plugins/mapped.py", line 321, in apply_mapping_filter 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application utils.validate_mapped_group_ids(group_ids, mapping_id, identity_api) 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application TypeError: 'method' object is not subscriptable 2023-12-13 09:42:54.920 1084357 ERROR keystone.server.flask.application

My current setup:
Google (user and groups membership) OIDC -> Keycloak OIDC -> Keystone

Mapping rules (without parameters related to group settings works fine):
[
    {
        "local": [
            {
                "user": {
                    "name": "{0}",
                    "email": "{1}",
                    "domain": {
                        "id": "default"
                    }
                }
            },
            {
                "group": {
                    "name": "{2}",
                    "domain": {
                        "id": "default"
                    }
                }
            }
        ],
        "remote": [
            {
                "type": "OIDC-preferred_username"
            },
            {
                "type": "OIDC-email"
            },
            {
                "type": "OIDC-usergroups"
            }
        ]
    }
]

WSGI Apache:
## WSGI configuration
  WSGIApplicationGroup %{GLOBAL}
  WSGIDaemonProcess keystone display-name=keystone group=keystone processes=4 threads=1 user=keystone
  WSGIProcessGroup keystone
  WSGIScriptAlias / "/usr/lib/cgi-bin/keystone/keystone"
  WSGIPassAuthorization On
  LoadModule auth_openidc_module modules/mod_auth_openidc.so
  OIDCClaimPrefix "OIDC-"
  OIDCResponseType "id_token"
  OIDCScope "openid email profile"
  OIDCProviderMetadataURL "https://keycloak.yyyy.zzz/realms/master/.well-known/openid-configuration"
  OIDCClientID "openstack"

Any help appreciated! 

Thanks in advance,
Franciszek Przewoźny