Access nested fields in JWT token for [subject_key, roles_key]

Hi. I’m trying to connect to ES via JWT with an access token in the header.

My config and token are below. Currently es is failing to retrieve given a nested key. Unfortunately, I don’t have the ability to re-design and re-implement the token structure.

Any ideas?

esproxy-service      | [2019-05-31T22:48:39,068][WARN ][c.a.d.a.h.j.HTTPJwtAuthenticator] [f9hNS8q] Failed to get subject from JWT claims, check if subject_key 'context.user.name' is correct.
esproxy-service      | [2019-05-31T22:48:39,069][ERROR][c.a.d.a.h.j.HTTPJwtAuthenticator] [f9hNS8q] No subject found in JWT token
```

      jwt_auth_domain:
        enabled: true
        http_enabled: true
        transport_enabled: true
        order: 0
        http_authenticator:
          type: jwt
          challenge: false
          config:
            signing_key: "XXXXXXXXX"
            jwt_header: "Authorization"
            jwt_url_parameter: null
            subject_key: context.user.name
            roles_key: context.user.policies
        authentication_backend:
          type: noop
```



```
{
  "context": {
    "user": {
      "policies": [
        "data_upload",
        "programs.XXXX-read-storage",
        "programs.XXXX-read",
        "programs.XXXX-create",
        "programs.XXXX-upload",
        "programs.XXXX-update",
        "programs.XXXX-delete",
        "programs.XXXX.proj1-read-storage",
        "programs.XXXX.proj1-read",
        "programs.XXXX.proj1-create",
        "programs.XXXX.proj1-upload",
        "programs.XXXX.proj1-update",
        "programs.XXXX.proj1-delete",
        "programs.XXXX.proj2-read-storage",
        "programs.XXXX.proj2-read",
        "programs.XXXX.proj2-create",
        "programs.XXXX.proj2-upload",
        "programs.XXXX.proj2-update",
        "programs.XXXX.proj2-delete",
        "programs.XXXX.proj3-read-storage",
        "programs.XXXX.proj3-read",
        "programs.XXXX.proj3-create",
        "programs.XXXX.proj3-upload",
        "programs.XXXX.proj3-update",
        "programs.XXXX.proj3-delete"
      ],
      "google": {
        "proxy_group": null
      },
      "is_admin": true,
      "name": "ME@XXXX.edu",
      "projects": {
        "XXXX-proj1": [
          "read-storage",
          "read",
          "create",
          "upload",
          "update",
          "delete"
        ],
        "XXXX-proj3": [
          "read-storage",
          "read",
          "create",
          "upload",
          "update",
          "delete"
        ],
        "XXXX": [
          "read-storage",
          "read",
          "create",
          "upload",
          "update",
          "delete"
        ]
      }
    }
  },
  "jti": "e49fd918-7a4e-4f02-a03b-f56f72a74d87",
  "aud": [
    "openid",
    "user",
    "credentials",
    "data",
    "admin",
    "google_credentials",
    "google_service_account"
  ],
  "exp": 1559343582,
  "azp": "",
  "iss": "https://localhost/user",
  "iat": 1559342382,
  "pur": "access",
  "sub": "2"
}
```

We’re you able to solve this issue with nested role key? Is it a bug or perhaps the roles_key syntax is different? It would be nice to have an example in the docs.

I am hitting a similar issue when using keycloak/openid-connect auth.

I am seeing this log:

[2019-11-08T16:24:47,248][WARN ][c.a.d.a.h.j.AbstractHTTPJwtAuthenticator] [elasticsearch-master-0] Failed to get roles from JWT claims with roles_key 'roles'. Check if this key is correct and available in the JWT payload.

using this securityconfig/config.yml authc section:

      openid_auth_domain:
        http_enabled: true
        transport_enabled: true
        order: 1
        http_authenticator:
          type: openid
          challenge: false
          config:
            subject_key: preferred_username
            roles_key: resource_access.es.roles
            openid_connect_url: https://my.keycloak.server/auth/realms/myRealm/.well-known/openid-configuration
        authentication_backend:
          type: noop

and my jwt token looks like this:

{
  "jti": "ab478371-4238-4499-9ed0-a8386540acb0",
  "exp": 1573170280,
  "nbf": 0,
  "iat": 1573169980,
  "iss": "https://my.keycloak.server/auth/realms/myRealm",
  "aud": "account",
  "sub": "7345affe-da01-420a-b0fc-9d7541f24b7c",
  "typ": "Bearer",
  "azp": "es",
  "auth_time": 0,
  "session_state": "10ccc236-716c-4b24-a46a-3e6168a986f4",
  "acr": "1",
  "realm_access": {
    "roles": [
      "offline_access",
      "uma_authorization"
    ]
  },
  "resource_access": {
    "account": {
      "roles": [
        "manage-account",
        "manage-account-links",
        "view-profile"
      ]
    },
    "es": {
      "roles": [
        "test"
      ]
    }
  },
  "scope": "profile email",
  "email_verified": false,
  "name": "Nathaniel Gentile",
  "preferred_username": "nate.gentile@whiteblock.io",
  "given_name": "Nathaniel",
  "family_name": "Gentile",
  "email": "nate.gentile@whiteblock.io"
}

I looked at code and what I found so far is

  1. source code in opensearch security project
  2. code is using 3rd party lib to read the roles form token : ‘io.jsonwebtoken:jjwt-api’
  3. processing code is in HTTPJwtAuthenticator.java
String rolesKey = settings.get("roles_key");
Claims claims = jwtParser.parseClaimsJws(jwtToken).getBody();
Object rolesObject = claims.get(rolesKey, Object.class);
String[] roles = String.valueOf(rolesObject).split(",");

So the property value is read into a String ( Settings class in in OS parent project)
And roles are extracted from Claims using that String.

So the question is how the JWTParser parses the token into Claims and with what String value we can get the roles out.

Workaround is to make your IDP service (assuming Keycloak) put the realm_access.roles to just roles key at root level. In Keycloak Client settings → Mappings → Create Build-in Mapping → select Realm Access and then change the Token KEy field form default “realm_access.roles” to just “roles”.

But I too would still prefer to just configure the OS to read the default roles location.

P.S. If you use
roles_key: realm_acccess
it gets a messed up string value json object

any update on this?
is there any plan to support nested fields in JWT?

Hi @manoj,

I did some digging in GitHub - it looks like some work has been done to correct this behaviour: config.yml settings for OpenID Connect not working with nested hierarchy for roles · Issue #551 · opensearch-project/security · GitHub
However, it does not look like it was concluded.

If you are experiencing this behaviour, please feel free to file an issue in the GitHub: OpenSearch Project · GitHub

Best,
Mantas

Thanks for the update @Mantas .

It was discussed in that issue, but closed without any code merges.
Reopened the issue [FEATURE] Add support for nested roles in OIDC/JWT config (e.g. keycloak roles (realm_access.roles)) · Issue #3877 · opensearch-project/security · GitHub