OIDC Integration: {"statusCode":401,"error":"Unauthorized","message":"Unauthorized"}

Versions (relevant - OpenSearch/Dashboard/Server OS/Browser):
OpenSearch - 2.6.0
OpenSearch Dashboards - 2.6.0
docker-compose - 1.29.2
docker 20.10.17-ce

Describe the issue:
Hello together! :slight_smile:

I’m currently trying to get the OIDC integration to work for the OpenSearch Dashboards. But so far i think i’m a bit lost in all the possibilities the config and documentation is offering to me.

So basicially all i want to do is using an OIDC provider, and for testing purposes i have it all made within localhost. So e.g. i’m using port forwarding to open “https://localhost:5601” to get into my instance, also i’ve configured that host within my OIDC provider as redirect URL (to be exact, it is: “https://localhost:5601/auth/openid/login”).

It almost seems to work as i at least come to the login screen to this OIDC provider, which then brings me back to “https://localhost:5601/auth/openid/login?code=xxx”, but with the message in the subject of this thread.

My guess is, that the information coming back from the provider do not match with the expected claims/scopes. But i can not really verify that, as i couldn’t find out how to log that.


logging.verbose: true

Just brought me the following:

opensearch-node2         | [2023-04-21T12:45:31,138][WARN ][o.o.s.h.HTTPBasicAuthenticator] [opensearch-node2] No 'Basic Authorization' header, send 401 and 'WWW-Authenticate Basic'
opensearch-node2         | [2023-04-21T12:45:31,138][WARN ][o.o.s.a.BackendRegistry  ] [opensearch-node2] No 'Authorization' header, send 401 and 'WWW-Authenticate Basic'
opensearch-dashboards    | {"type":"log","@timestamp":"2023-04-21T12:45:31Z","tags":["error","plugins","securityDashboards"],"pid":1,"message":"OpenId authentication failed: Error: Authentication Exception"

I do have two valid users to login to my backend. The admin user and a user i’ve added to my internal users file.

Do i have to setup some kind matching between the internal user and the claims coming from the OIDC provider? If yes, where and how can i do that the best way? Also the message from above claims, that no Authorization header has been sent. Did i miss something here?

Furthermore, i didn’t really understood the dependencies between config.yml and opensearch-dashboards.yml and why i need to configure e.g. the connect url two times.

Any help or hint is highly appreciated :slight_smile:



server.host: ""
server.name: opensearch-dashboards
opensearch.username: "admin"
opensearch.password: "admin"
server.ssl.enabled: true
server.ssl.certificate: "/usr/share/opensearch-dashboards/config/certificates/os-dashboards/os-dashboards.pem"
server.ssl.key: "/usr/share/opensearch-dashboards/config/certificates/os-dashboards/os-dashboards.key"
opensearch.ssl.certificateAuthorities: ["/usr/share/opensearch-dashboards/config/certificates/ca/ca.pem"]
opensearch.ssl.verificationMode: none
opensearch_security.cookie.secure: true
opensearch.requestHeadersAllowlist: ["Authorization"]
opensearch_security.multitenancy.enabled: true
opensearch_security.multitenancy.tenants.preferred: ["Global"]
 #enabled both to see if it is still working
opensearch_security.auth.type: ["basicauth","openid"]
opensearch_security.auth.multiple_auth_enabled: true
opensearch_security.openid.connect_url: "https://thefancyprovider/.well-known/openid-configuration"
opensearch_security.openid.client_id: "<censored>"
opensearch_security.openid.client_secret: "<censored>"
opensearch_security.openid.scope: "openid entitlement_group"
opensearch_security.openid.header: "Authorization"
opensearch_security.openid.logout_url: "https://localhostlogouturl/logout"
opensearch_security.cookie.isSameSite: None
opensearch_security.openid.verify_hostnames: false
opensearch_security.openid.base_redirect_url: "https://localhost:5601"
opensearch_security.openid.trust_dynamic_headers: true
opensearch_security.openid.refresh_tokens: true


      anonymous_auth_enabled: false
        description: "Authenticate via HTTP Basic against internal users database"
        http_enabled: true
        transport_enabled: true
        order: 0
          type: basic
          challenge: false
          type: intern
        description: "OpenID connection"
        http_enabled: true
        transport_enabled: true
        order: 1
          type: openid
          challenge: false
            subject_key: sub
            roles_key: entitlement_group
            openid_connect_url: https://thefancyprovider/.well-known/openid-configuration
              - admin
          type: noop

Relevant Logs or Screenshots:

Okay well, it was as i thought. I had to map the claim sub to my internal_users.yml file.

inside the sub it looked like the following:


But i didn’t thought about any case sensitivity. As my internal_users.yml config looked like:

hash: “”

so i’ve changed from userid to USERID and now i can login with my user.


  1. That way i can securely deactivate the hash, right? As a password is not necessary anymore?
  2. Even if the login is working, i’m receiving the following errors:
opensearch-node1         | [2023-04-25T07:00:19,339][WARN ][o.o.s.h.HTTPBasicAuthenticator] [opensearch-node1] No 'Basic Authorization' header, send 401 and 'WWW-Authenticate Basic'
opensearch-node1         | [2023-04-25T07:00:19,340][WARN ][c.a.d.a.h.j.AbstractHTTPJwtAuthenticator] [opensearch-node1] Failed to get roles from JWT claims with roles_key 'entitlement_group'. Check if this key is correct and available in the JWT payload.
opensearch-node3         | [2023-04-25T07:00:19,343][WARN ][o.o.s.h.HTTPBasicAuthenticator] [opensearch-node3] No 'Basic Authorization' header, send 401 and 'WWW-Authenticate Basic'
opensearch-node3         | [2023-04-25T07:00:19,344][WARN ][c.a.d.a.h.j.AbstractHTTPJwtAuthenticator] [opensearch-node3] Failed to get roles from JWT claims with roles_key 'entitlement_group'. Check if this key is correct and available in the JWT payload.
opensearch-node2         | [2023-04-25T07:00:19,348][WARN ][o.o.s.h.HTTPBasicAuthenticator] [opensearch-node2] No 'Basic Authorization' header, send 401 and 'WWW-Authenticate Basic'
opensearch-node2         | [2023-04-25T07:00:19,348][WARN ][c.a.d.a.h.j.AbstractHTTPJwtAuthenticator] [opensearch-node2] Failed to get roles from JWT claims with roles_key 'entitlement_group'. Check if this key is correct and available in the JWT payload.

@B3n You don’t need internal users with OIDC authentication. The idea is to have your OIDC users managed by an external IdP and not the OpenSearch cluster.

The missing part is the lack of role mapping. The groups/roles assigned to the user in the IdP should be mapped to the OpenSearch’s roles as backend roles.
To achieve that you need to configure that mapping in the roles_mapping.yml file.

What is your IdP?

Thanks for your reply @pablo :slight_smile:

I’m using Ping Identity as IdP.

For my own understanding: When i want to use an external OIDC provider i have to define roles with their specific permissions and then define the mappings inside roles_mapping.yml with backend_roles that match to the roles coming from the IdP?

As i’m using a docker-compose setup where i include the file “internal_users.yml”, this makes the include of this file obsolete, right?

So i suppose the challenge here is to get the roles correctly out of the IdP. Usually the roles are entitlements and are stored inside the claim “entitlement_group”

But the error e.g.

opensearch-node2 | [2023-04-25T07:00:19,348][WARN ][c.a.d.a.h.j.AbstractHTTPJwtAuthenticator] [opensearch-node2] Failed to get roles from JWT claims with roles_key 'entitlement_group'. Check if this key is correct and available in the JWT payload.

confuses me. Can i somehow enable a logging to see the actual payload coming from the IdP?


Correct, unless you wish to have some custom internal users (i.e. logstash, filebeat, metricbeat etc.)


This error states that entitlement_group was not found in the JWT token sent by the IdP. Therefore, the security plugin couldn’t map the IdP roles with the OpenSearch roles.

Unfortunately, OpenSearch is lacking OpenID token logging.
Have you tried the approach described in this PingIdentity document?



Thanks a lot for your clarification!

Unfortunately i do not have any access to the ping id setup, and i only have limited access in general. So i suppose i can’t dig any deeper on that and use anything roles_key related :frowning:

@B3n There is another option. You can use PingID username instead of the role name in the roles_mapping.yml


  reserved: false
  - "<pingid_user>"

It’s not a perfect solution but at least will keep you away from internal users. This user field accepts also regular expressions. For example, if your users have a common phrase in their usernames then you can make this role assignment more flexible.


  reserved: false
  - "*"
1 Like

Hey @pablo!

That works perfectly fine for me, thanks a lot for your input and help! :slight_smile:

1 Like