vault icon indicating copy to clipboard operation
vault copied to clipboard

OIDC auth - customise entity names

Open ccakes opened this issue 6 years ago • 11 comments

Is your feature request related to a problem? Please describe. Follow on from the discussion at the end of https://github.com/hashicorp/vault/issues/7723

OIDC providers generally should use the sub claim to uniquely identify the user, so when using the OIDC auth method with Vault it makes sense to use that for the user_claim setting. This means however that we get unrecognisable entity names being created.

Using the email claim gives useful names but at the cost of not guaranteeing that a user will keep the same identity.

Describe the solution you'd like Since we can already use claim_mappings to copy data from claims into the Entity metadata, one option could be to have a reserved metadata field (eg. entity_name) which would be hoisted on to the Entity list. This way I could copy the email claim into that and get a readable entity list

Describe alternatives you've considered Using the email claim, however people do change their names and the email value from the IdP isn't guaranteed to be consistent for the same user.

ccakes avatar Nov 25 '19 19:11 ccakes

OIDC providers generally should use the sub claim to uniquely identify the user, so when using the OIDC auth method with Vault it makes sense to use that for the user_claim setting. This means however that we get unrecognisable entity names being created.

I believe that's not quite correct, and it should say: "this means that we get unrecognized alias names being created".

Unless I've missed something, the entity which is dynamically created when the user first logs in always gets a randomly-generated name, like entity_0d526a1b. This is not affected by the choice of user_claim, nor any other alias metadata.

The alias name is the unique key from the upstream provider which identifies this alias within the scope of this mount point. If you have user_claim="sub" (which is best practice) then the alias name will be unrecognizable. If you use user_claim="email" then the alias name is recognizable - but the name of the entity created will still be entity_0d526a1b or whatever. In either case you can map claims like email and name to alias metadata (but not entity metadata).

Now, what I believe you're proposing is that the initial entity name be derived from OIDC claim data, and if so, I support that idea. Entities can be listed by name, and it is much more useful if they have sensible names by default.

I believe that claim uniqueness isn't a problem. If Vault is creating a dynamic entity from a new alias (i.e. first-time login), but the proposed entity name from an OIDC claim is already taken, then it can fall back to using a randomly-generated name instead.


There is one other related issue I came across, which is the token display_name. I'm fairly sure this is what the user sees in the top-right corner of the Vault UI:

image

As far as I can tell, it is hard-coded[^1] that the display_name is the alias name. Hence if you use user_claim="sub" then this is what they see, which is not very user-friendly.

I would like it to be possible for the token display_name to be expanded from alias metadata and/or from entity metadata, e.g. {{ identity.entity.name }} or {{ identity.alias.metadata.email }}

The token display_name may also appear in logs. I note that the alias name by itself is not guaranteed to be unique (because in principle, two different upstream OIDC providers could return the same sub value)

[^1]: user_claim sets the alias Name, which sets DisplayName.

candlerb avatar Feb 24 '22 11:02 candlerb

+1 to this one. I'm subscribed to this issue since 2019 but see no improvements here.

There are multiple issues related to this one.

OIDC claims used as external id (sub, email).

sub claim is hardly-readable, machine-oriented, and typically immutable id. email claim is generally readable, mostly human-oriented, and potentially mutable id. So a "right" way to create an alias would be by picking sub as an identifier binding to an entity, and email as something displayed in UI. This affects multiple places:

  • Displayed name in the top right corner mentioned by @candlerb. I think we need to display a human-readable piece that identifies the logged-in Entity here (not alias) in the first place. The issue is that by default Entity doesn't have such a piece as its name is some random id (more below). Even better if we see here both entity and entity alias used to login (maybe both sub and email. It would be really awesome if this can be templated like {{identity.entity.name}}\n{{identify.alias.metadata.name}}\n{{identity.alias.name}}. The last piece would be mapped sub.

  • Both entities and entity aliases table views are pretty useless:

    • Entity entry typically contains
      • a random name, e.g. entity_3384d62f
      • a random vault id, e.g.1889c86f-aace-f709-117e-a5edd8de32b1.
    • Entity alias entry (if using sub user_claim) contains:
      • a random name (equal to sub claim), e.g. Mzk3NTESBDY2OTE1Nzcmdvb2dDE1NjQChUxM30NsZQ
      • a random vault id, e.g. 4aec187a-3e94-f25f-e7e0-39d4cedd9f93
      • a random accessor, e.g. auth_oidc_94b89bee

    Effectively, both views provide zero human-readable information to users. Machine-readable stuff is fine to display as well, but the expected are common identifiers like oidc email, oidc user name, probably even profile picture. I guess this can be solved similarly to top-right menu with template view.

Display name in top-right menu.

This has been well explained by @candlerb

Allowing personal secrets to users

ACL may look like this:

# Personal secrets
path "secret/data/users/{{identity.entity.name}}/*" {
  capabilities = ["create", "update", "read", "delete", "list"]
}

path "secret/metadata/users/{{identity.entity.name}}/*" {
  capabilities = ["list"]
}

But the issue is that the default entity.name is not human readable. I can do something like secret/data/users/{{identity.entity.aliases.auth_oidc_18e94ee9.name}}. That works but is a bit different thing.

As a side note: it would be really awesome if somehow extracting just a username from email was possible.

Corellating new entity alias with entity

Two problems here:

  • How to correlate a newly created alias with an existing entity
  • If an existing entity is not found, how to create a new entity

The proposal here is to have two fields associated with (most?, all?) auth methods (e.g. set in JWT/OIDC Role, or LDAP config):

  • An array of correlated paths from alias to entity. Each element has two components: entity alias json path and entity json path.
  • The second is a JSON path or better ACL-like template that renders a new entity name. If such name exists, some resolution mechanism is used, e.g. by appending a suffix with number: john_doe (2).

Example:

{
  entity_match: [
    {alias: "name", entity: "/metadata/sub"}, // or with dot notation "metadata.sub"
    {alias: "/metadata/name", entity: "/metadata/fullname"} // this is only possible if fullname in both cases is fully controlled and set by admin
  ],
  entity_name: "{{metadata.email}}" // new entity name. In my case, I would like just a user name part from the email
}

I do understand that this won't solve complex entity merging issues with multiple identity providers, but still may be good enough for most simple cases, like mapping email in fields with different names.

ksa-real avatar Jul 20 '22 13:07 ksa-real

ACL may look like this:

# Personal secrets
path "secret/data/users/{{identity.entity.name}}/*" {
  capabilities = ["create", "update", "read", "delete", "list"]
}

path "secret/metadata/users/{{identity.entity.name}}/*" {
  capabilities = ["list"]
}

But the issue is that the default entity.name is not human readable.

Well, one option would be to use secret/metadata/users/{{identity.entity.id}}/* - the entity id is still not human readable, but at least it's stable and cannot be forged or duplicated. It is the primary key of the entity after all.

As a separate issue, having meaningful entity names: I would say that we should avoid creating random entities in the first place (in particular as a side-effect of a "drive-by" first-time authentication).

If you want someone to have access to a system, then you should explicitly create them as an entity and explicitly grant them access - in which case, you can choose their entity name to be meaningful at the time you create them, and there are no longer any random entity names.

However, if you do create an entity in advance like this, then you need some way to create the corresponding entity alias which allows the user to authenticate. I raised this separately as #14989.

candlerb avatar Jul 20 '22 15:07 candlerb

I want to keep Google Auth as a source of truth for groups and entries. I already explicitly created a user [email protected] and added them to a group [email protected]. I just want to create some rules for Vault once and don't do something in Vault for every user.

I disagree with the requirement to explicitly create an entity. I want to use Vault for secrets and not as a source-of-truth IdP. For that, I want to set up rules for creating "Entities" as a cache of Google Auth entries and assign policies based on Google Groups.

I can also totally imagine a similar use case: e.g. users can come from Google or Github. Admin wants to allow users to create their own secrets in secret/users/<user email>. Both Google and Github can provide email in JWT. To me, it is pretty logical to create an entity with email as a name (instead of entity_XXXXXX).

ksa-real avatar Jul 20 '22 15:07 ksa-real

I want to use Vault for secrets and not as a source-of-truth IdP. For that, I want to set up rules for creating "Entities" as a cache of Google Auth entries and assign policies based on Google Groups.

Sure: if you have a Google Workspace domain then you can do that using external groups. But then there's not much value using Vault as your intermediate IdP; you might as well use something dumb and stateless like DeX.

If you treat Google as the source of truth, then for example you couldn't have a single user (entity) who can login using either their Google account or their Office365 account, by having one Vault entity linked to two aliases.

candlerb avatar Jul 20 '22 16:07 candlerb

I want to use Vault for secrets and not as a source-of-truth IdP. For that, I want to set up rules for creating "Entities" as a cache of Google Auth entries and assign policies based on Google Groups.

Sure: if you have a Google Workspace domain then you can do that using external groups. But then there's not much value using Vault as your intermediate IdP; you might as well use something dumb and stateless like DeX.

Which wasn't a goal :)

If you treat Google as the source of truth, then for example you couldn't have a single user (entity) who can login using either their Google account or their Office365 account, by having one Vault entity linked to two aliases.

Why not? Google Groups information won't propagate if logging in via Office365 the first time. Technically it can, but let's assume this is a different auth engine, so it cannot. So it would be inconvenient to use Google as a source of truth for groups. But both sources are a source of truth for user email. So it would be fine to auto-create an entity based on either source's email. After or obviously before the first login we can do a group assignment.

ksa-real avatar Jul 20 '22 16:07 ksa-real

Groups are very messy in this scenario.

When using external groups, then when the user logs in with their Google account they'll only get their Google groups, and ditto for Office 365. Now, you can create a single group in Vault, and link it via two group aliases, once to a Google group and once to an O365 group. Obviously, you'll have to make sure that every time you add a user to a group in Google's admin system, you'll have to add them to the corresponding group in O365. That would be too painful (and dangerous) for me. I'd want a single source of truth for group memberships.

candlerb avatar Jul 20 '22 16:07 candlerb

This is what I said. Just separate the entity (user) and group assignment. Entity CAN be created in this case automatically (with no permissions). Groups can be assigned later in Vault (internal groups). Vault can be useful even before the groups are assigned.

ksa-real avatar Jul 20 '22 16:07 ksa-real

I'm not sure I follow.

If a user is created automatically (say entity_abcd1234), but has no groups until you manually add this entity to (internal) groups, then they're not authorized to do anything until the group memberships are set. Hence the account is not "useful" at that point.

OTOH, if a user is created automatically and has external groups, then their group membership only works if they login using that one upstream IDP.

candlerb avatar Jul 20 '22 16:07 candlerb

Do you want to talk about how it is done right now or how it can be done? Assume we are assigning all authorized users the authorized policy.

path "secret/data/users/{{identity.entity.name}}/*" {
  capabilities = ["create", "update", "read", "delete", "list"]
}

path "secret/metadata/users/{{identity.entity.name}}/*" {
  capabilities = ["list"]
}

path "secret/data/users/+/share/{{identity.entity.name}}/*" {
  capabilities = ["read", "delete"]
}

path "secret/metadata/users/+/share/{{identity.entity.name}}/*" {
  capabilities = ["list"]
}

Now all authorized users can share secrets using their and other people's entity names. This is something useful and no groups are assigned yet to a user. What is wanted:

  • When the user logs in with Github for the first time, the entity is created with the email as a name. Github entity alias added to the entity.
  • When later the user logs in with Google for the first time, Google entity alias is automatically added to the previously created entity because new entity alias (email) matches entity name.
  • As an admin, I can assign groups to the user's entity at any time: either before the Github login, by explicitly manually creating the entity, or later by simply assigning a group.

ksa-real avatar Jul 20 '22 16:07 ksa-real

Hi Team, what is the status on this one? I think this would be a really useful feature

shanewhite97 avatar Dec 13 '23 08:12 shanewhite97