dex icon indicating copy to clipboard operation
dex copied to clipboard

Support for an arbitrary set of claims (including user defined claims)

Open cstrahan opened this issue 7 years ago • 16 comments

Dex currently has support for some particular set of claims, and that choice is reflected in various rigid data structures and e.g. the queries that make up the persistence backends (see storage/sql/crud.go for example). Each time a new claim is to be supported by Dex, the Dex maintainers must touch many files and many data structures. This is tedious work that could be obviated by a design change where an arbitrary set of claims can be plumbed through the system, and the storage backends would be changed such that they have no prior, hard coded knowledge about some set of predefined claims. There are a number of issues that are currently open proposing changes to the supported claims, and each would be easier to implement under this proposed design change:

  • #1052 - Support auth_time in id_token claims
  • #921 - ID-token does not propagate 'preferred_username' from Azure ID Token
  • #892 - Include connector UserID in ID Token
  • #552 - Map LDAP attributes to claims
  • etc

In addition to easing the development burden for the Dex maintainers, this would have the added benefit of making it trivial to allow third party connector developers to specify additional claims of their own choosing.

This is something I could contribute, if this would be a welcome change.

cstrahan avatar Feb 02 '18 17:02 cstrahan

I would be happy if we can support at least the OpenID Standard claims 5.1 Standard Claims

ChSch3000 avatar May 16 '18 09:05 ChSch3000

For what it's worth (though, full-disclosure: I also work @DecipherNow) I also need this before we can use Dex to front identity providers that specify (potentially complex) user security attributes in custom claims. I would also be happy to help contribute the necessary improvements with @cstrahan, if we could be reasonably certain of general interest.

danielpcox avatar Oct 29 '18 17:10 danielpcox

if we could be reasonably certain of general interest.

Well there's 10 :+1: on this issue now. :man_shrugging: :smiley:


I'm desiring to use Dex for a Hasura authentication backend, but the issue is that Hasura expects certain specific claims that Dex will not provide. I need to be able to take the claims that Dex can derive such as username and groups and transform them into the Hasura accepted claims.

For example I need to go from this:

{
  "iss": "http://123.456.789.100:5556/",
  "sub": "Caieoruqopier",
  "aud": "testapp",
  "exp": 1593188378,
  "iat": 1593101978,
  "nonce": "qpoireioquweiorpq",
  "at_hash": "t-asdfjalskdfjl",
  "email": "[email protected]",
  "email_verified": true,
  "groups": [
    "admin",
    "staff"
  ],
  "name": "Dude"
}

And I need to convert it to:

{
  "iss": "http://123.456.789.100:5556/",
  "sub": "Caieoruqopier",
  "aud": "testapp",
  "exp": 1593188378,
  "iat": 1593101978,
  "nonce": "qpoireioquweiorpq",
  "at_hash": "t-asdfjalskdfjl",
  "email": "[email protected]",
  "email_verified": true,
  "groups": [
    "admin",
    "staff"
  ],
  "name": "Dude",
  "https://hasura.io/jwt/claims": {
    "x-hasura-allowed-roles": ["admin", "staff"],
    "x-hasura-default-role": "admin",
    "x-hasura-user-id": "[email protected]"
  }
}

I would need some way to create fill out these custom claims, though. I'm thinking something like this in the config YAML:

- type: github
   # Provider options...
   # Custom claims could be used on any provider type
   customClaims:
     https://hasura.io/jwt/claims:
       # Use go template language for values and the existing claims as inputs
       # to the template.
       x-hasura-allowed-roles: {{ groups }}
       x-hasura-default-role: {{ index groups 1 }}
       x-hasura-user-id: {{ email }}

zicklag avatar Jun 25 '20 16:06 zicklag

Update: Just found this PR ( https://github.com/hasura/graphql-engine/pull/3575 ) in Hasura which makes this unnecessary for my particular use-case, I think, but I still think that custom claims would be a great feature.

zicklag avatar Jun 25 '20 17:06 zicklag

Now that I think about it, I could still use a way to get more information about the user from certain backends like LDAP. For example, I want to know the users username ( in LDAP it's the uid ) and their full name ( in LDAP it's the cn ), but I'm only allowed to grab one value for the name with the current LDAP connector.

I think different connectors would need different connector-specific ways of adding custom claims that are based on the connector data ( such as LDAP ), but all connectors could support static/templated custom claims like the YAML example above.

zicklag avatar Jun 25 '20 20:06 zicklag

At the simplest level, custom claims could configured explicitly:

  • per user - using the (provider, sub) pair as the key
  • per group - using the (provider, group) pair as the key

and stored either in YAML or in a SQL/etcd backend (with API maintenance). That would cover my use cases, as long as custom claims includes being able to add custom groups.

It looks like there are two further suggestions:

  • algorithmic mapping of claims to claims, such as the x-hasura-user-id example above
  • algorithmic mapping of source database attributes to claims, such as arbitrary LDAP attributes

I think those would be done most flexibly by spawning a child process which takes JSON on stdin and returns updated or additional claims on stdout. Then it can be written in whatever language you like, and use whatever data sources you like.

candlerb avatar Jun 26 '20 04:06 candlerb

What is the status of this issue? Is there currently a possibility to create custom claims or are you planning to implement this feature?

I would need this feature as minIO needs custom claim "policy" (possible values: "writeonly", "readonly", "readwrite").

KairaMiu avatar Oct 16 '20 06:10 KairaMiu

I would need this feature as minIO needs custom claim "policy" (possible values: "writeonly", "readonly", "readwrite").

FWIW - You can configure custom claims @KairaMiu

docs/sts/dex.md:export MINIO_IDENTITY_OPENID_CLAIM_NAME=groups

harshavardhana avatar Oct 16 '20 17:10 harshavardhana

Not all IDPs let you choose the names of your groups though. For example, using Azure AD as your IDP, I believe all the groups are UUIDs. Maybe you can rename your STS policies with those UIUD names, but it's not ideal.

In general, I see a tension between IDPs and applications:

  • Some IDP providers only provide specific standard claims. They expect that the application will do all authorization (configured based on individual user identities, or group memberships)
  • Some IDP consumers (applications) expect the IDP to provide specific authorization claims to meet their needs

I see Dex in the middle as a way to resolve this, if it can add extra claims for specific users and/or specific groups - perhaps scoped to individual consuming applications, so that other apps don't see irrelevant or conflicting claims.

candlerb avatar Oct 17 '20 16:10 candlerb

I think Dex could probably help with this, #1635 could possibly provide an opportunity to alter claims, but I'm also concerned this is an infinitely deep rabbit hole we might not want to go down to. I wouldn't want to clutter Dex with application specific logic. As long as we can keep the core clean and provide extension points, I'm all in.

sagikazarmark avatar Oct 17 '20 20:10 sagikazarmark

I think that makes sense, although I'd also like to see either

  1. Extension of the existing storage engine and API to support applying claims to users and groups (including additional group memberships); or
  2. Separate out the build-in user/password management (CreatePassword, UpdatePassword, DeletePassword, ListPasswords, VerifyPassword) into a plugin. In that case, maybe LDAP becomes a plugin too.

This is to avoid having static users/passwords in one place managed by one API, while the claims and group memberships are stored in a different place managed by a different API.

candlerb avatar Oct 18 '20 10:10 candlerb

I have another concrete example of where custom claims could be used.

PostgREST, a REST front-end to Postgres, uses JWT claims to perform authorization. Specifically, it requires:

{
    "role": "<postgres-role>"
}

to switch to a particular postgres role for this user. (The code inside the database can also look at additional claims such as "email" if required, but "role" is mandatory to start this process)

Therefore, it would be extremely useful if I could attach this claim to specific users and/or groups.

This also makes something clear to me: adding such custom claims should be specific to the Dex client (audience). It's pointless adding a PostgREST "role" claim for tokens destined to a different audience; and in the worst case, two different apps may use the "role" claim for different purposes.

Hence I think custom claims should be attached to (provider, sub, audience) and (provider, group, audience) [^1][^2]

To be most useful, I believe these custom claims should be in Dex's own data store (i.e. etcd, sqlite or whatever). These could then be managed via the gRPC interface. Whilst the LDAP backend could also be extended for custom claims, I consider the LDAP backend to be a "connector" like the Github or Google connectors: a remote source of truth, which you may not want to meddle with (e.g. a corporate Active Directory where you are not allowed to alter the schema).


[^1] Or more generally: (provider, claim_name, claim_value, audience). You could then match arbitrary claims in the JWT provided by the upstream connector.

[^2] There needs to be some rule for what happens if there are conflicts, e.g. a user is member of two different groups which have different values for the same claim. Maybe there should be a priority value attached to the row, and highest priority wins.

It's possible also that someone might want to assemble a multi-valued list claim as a list. The most likely use case I can see for that is updating or replacing the "group" claim itself - e.g. if a user is a member of group "some-long-uid" from Azure Active Directory, then make them a member of group "admins" for a particular client application.

candlerb avatar Feb 11 '21 11:02 candlerb

FYI, the proposed middleware implementation in PR #1841 extends the connector.Identity object with

	CustomClaims map[string]interface{}

candlerb avatar Mar 13 '21 17:03 candlerb

Azure AD can emit a roles claim in the id_token containing all application roles assigned to the user. Would be really nice to be able to funnel through those to be used for RBAC mapping as a complement to the groups claim. https://docs.microsoft.com/en-us/azure/active-directory/develop/howto-add-app-roles-in-azure-ad-apps

Fresa avatar May 05 '21 06:05 Fresa

Reading this issue it does seem there is currently no way of getting Dex to pass the (custom) claims I need to obtain from the IDP I want to connect to? (also seen here: https://github.com/dexidp/dex/discussions/2596)

So my IDP supports these claims:

"claims_supported": [
    "nickname",
    "edupersontargetedid",
    "sub",
    "eppn",
    "email",
    "schac_home_organisation",
    "idp"
  ],

These claims also won't show up in the federated claims (when using federated:id in the scope):

"userinfo": {
        "at_hash": "HNEIJE63kYFP7OvO_bUocQ",
        "aud": "my-app",
        "c_hash": "asdfEu_Td62dh5QlTM_tQ",
        "email": "[email protected]",
        "email_verified": true,
        "exp": 1746870878,
        "federated_claims": {
            "connector_id": "satosa",
            "user_id": "2345324534253425"
        },
        "iat": 1746784478,
        "iss": "https://my-dex-idp.whatever.com",
        "name": "John Doe",
        "nonce": "ZlnKWeFK9iJ03RGoCIvR",
        "sub": "CigyYzYzYmFmasdAEYYQ0NTQzMjFmReE1NDNE3234CCM1YWY3MDkxEgZzYXRvc2E"
    }

Even hacking the aud claim to use take the value of the idp claim does not seem to work (I also tried mapping these custom claims to other openid standard claims, but they all won't work):

getUserInfo: true
              claimMapping:
                aud: idp  # ugly! hack the aud with the idp field
              overrideClaimMapping: true

jblom avatar May 09 '25 11:05 jblom

A tiny, ugly hack https://github.com/edgeflare/dex/commit/190081f5e85580ef0cf28c711e3b9afd5a1f2a80

make build
export DEX_CUSTOM_CLAIMS_STATIC='{"policy": {"pgrole": "authn"}, "anotherclaim": "value"}'
./bin/dex serve config-dev.yaml

hmoazzem avatar May 09 '25 14:05 hmoazzem