I’m facing the same issue with OpenID like many others. Checked a lot of related topics here, but have not found a solution. My Opensearch and Openseach-dashboards v. 2.10.
Keycloak is 26.2.
When I’m trying to login through Keycloak I get “401 Unauthorized with Keycloak OpenID”. In keycloak logs:
2025-08-20 11:51:28,371 WARN [org.keycloak.services] (executor-thread-139) KC-SERVICES0046: Multiple values found ‘[view-realm, view-identity-providers, manage-identity-providers, impersonation, create-client, manage-users, query-realms, view-authorization, query-clients, query-users, manage-events, manage-realm, view-events, view-users, view-clients, manage-authorization, manage-clients, query-groups]’ for protocol mapper ‘client roles’ but expected just single value
(as I can see in the log above there is no my os_role “os_admin“ in the list I created within the client opensearch1.
My keycloak client config attached:
{
"clientId": "opensearch1",
"name": "opensearch1",
"description": "",
"rootUrl": "https://keyvault.mycorp.local/",
"adminUrl": "https://keyvault.mycorp.local/",
"baseUrl": "",
"surrogateAuthRequired": false,
"enabled": true,
"alwaysDisplayInConsole": false,
"clientAuthenticatorType": "client-secret",
"secret": "vjbLwYK9ANfSWLmE7yxxoJic3pcFL16E",
"redirectUris": \[
"https://logging.mycorp.local/*"
\],
"webOrigins": \[
"https://logging.mycorp.local"
\],
"notBefore": 0,
"bearerOnly": false,
"consentRequired": false,
"standardFlowEnabled": true,
"implicitFlowEnabled": true,
"directAccessGrantsEnabled": true,
"serviceAccountsEnabled": true,
"authorizationServicesEnabled": true,
"publicClient": false,
"frontchannelLogout": true,
"protocol": "openid-connect",
"attributes": {
"realm_client": "false",
"oidc.ciba.grant.enabled": "false",
"client.secret.creation.time": "1755247233",
"backchannel.logout.session.required": "true",
"standard.token.exchange.enabled": "false",
"frontchannel.logout.session.required": "true",
"oauth2.device.authorization.grant.enabled": "false",
"display.on.consent.screen": "false",
"backchannel.logout.revoke.offline.tokens": "false"
},
"authenticationFlowBindingOverrides": {},
"fullScopeAllowed": true,
"nodeReRegistrationTimeout": -1,
"protocolMappers": \[
{
"name": "opensearch1-mapper",
"protocol": "openid-connect",
"protocolMapper": "oidc-usermodel-client-role-mapper",
"consentRequired": false,
"config": {
"introspection.token.claim": "false",
"multivalued": "true",
"userinfo.token.claim": "true",
"id.token.claim": "true",
"lightweight.claim": "false",
"access.token.claim": "true",
"claim.name": "os_roles",
"jsonType.label": "String",
"usermodel.clientRoleMapping.clientId": "opensearch1"
}
}
\],
"defaultClientScopes": \[
"web-origins",
"service_account",
"acr",
"profile",
"roles",
"basic",
"email"
\],
"optionalClientScopes": \[
"address",
"phone",
"offline_access",
"microprofile-jwt"
\],
"access": {
"view": true,
"configure": true,
"manage": true
}
}
My opensearch_dashboards.yml:
opensearch.hosts: \[http://localhost:9200\]
opensearch.ssl.verificationMode: none
opensearch.username: admin
opensearch.password: b6rb_nnm4_55Cx1
opensearch.requestHeadersWhitelist:
\["Authorization", "securitytenant", "WWW-Authenticate"\]
opensearch_security.multitenancy.enabled: true
opensearch_security.multitenancy.tenants.enable_global: true
opensearch_security.multitenancy.tenants.enable_private: true
opensearch_security.multitenancy.tenants.preferred: \[Global, Private\]
opensearch_security.multitenancy.enable_filter: false
opensearch_security.readonly_mode.roles: \[kibana_read_only\]
\# Use this setting if you are running opensearch-dashboards without https
opensearch_security.cookie.secure: false
\# keycloak:
opensearch_security.auth.multiple_auth_enabled: true
opensearch_security.auth.type: \["basicauth", "openid"\]
opensearch_security.ui.openid.login.buttonname: "Log in with Keycloak"
opensearch_security.openid.connect_url: https://keycloak.mycorp.local/auth/realms/master/.well-known/openid-configuration
opensearch_security.openid.base_redirect_url: https://logging.mycorp.local
opensearch_security.openid.client_id: opensearch1
opensearch_security.openid.client_secret: vjbLwYK9ANfSWLmE7yxxoJic3pcFL16E
opensearch_security.openid.verify_hostnames: false
opensearch_security.openid.refresh_tokens: false
#opensearch_security.openid.scope: "opensearch1-dedicated"
opensearch_security.openid.header: "Authorization"
opensearch_security.cookie.ttl: "3600"
My opensearch-security/config.yml:
authc:
\# <1>
basic_internal_auth_domain:
description: "Authenticate via HTTP Basic against internal users database"
http_enabled: true
transport_enabled: true
order: 0
http_authenticator:
type: basic
challenge: false
authentication_backend:
type: intern
\# <2>
openid_auth_domain:
http_enabled: true
transport_enabled: true
order: 1
http_authenticator:
type: openid
challenge: true
config:
subject_key: preferred_username
roles_key: os_roles
openid_connect_url: https://keycloak.mycorp.local/auth/realms/master/.well-known/openid-configuration
#scope: "openid profile email"
openid_connect_idp:
enable_ssl: true
verify_hostnames: false
jwt_clock_skew_tolerance_seconds: 60
client_id: opensearch1
client_secret: vjbLwYK9ANfSWLmE7yxxoJic3pcFL16E
authentication_backend:
type: noop
\#
And internal_users.yml:
---
\_meta:
type: "internalusers"
config_version: 2
\# Define your internal users here
admin:
hash: "$2y$12$S1.EVgMoneoDhT5\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*\*WFM9aKpIuDBD12d2"
reserved: true
backend_roles:
- "admin"
- "os_admin"
description: "Demo admin user"
kibanaro:
hash: "$2a$12$JJSXNfTowz7Uu5ttXfeYpeYE0arACvcwlPBStB1F.MI7f0U9Z4DGC"
reserved: false
backend_roles:
- "kibanauser"
- "readall"
- "os_kibanauser"
attributes:
attribute1: "value1"
attribute2: "value2"
attribute3: "value3"
description: "Demo OpenSearch Dashboards read only user, using external role mapping"
readall:
hash: "$2a$12$ae4ycwzwvLtZxwZ82RmiEunBbIPiAmGZduBAjKN0TXdwQFtCwARz2"
reserved: false
backend_roles:
- "readall"
- "os_readall"
description: "Demo readall user, using external role mapping"
Could somebody be so kind and help my find a solution?