WLCG-AuthZ-WG / common-jwt-profile

A repo for the WLCG Common JWT profile document
3 stars 8 forks source link

Propose a method for Group or Role Based Capability Selection #6

Closed jbasney closed 3 years ago

jbasney commented 3 years ago

This is my write-up of a request that CILogon has received from Fermilab with respect to our support for WLCG tokens. Interested in feedback from the group on this method.

jbasney commented 3 years ago

It's unclear to me if we want to require a wlcg.groups claim in the token for this scenario in addition to the scope claim. Currently the profile says for the wlcg.groups claim that "when requested it MUST be present in both token types" which implies that we'd need to include it even if our intention is to produce a token for capability-based authorization.

DrDaveD commented 3 years ago

The use case for this is for users that have multiple roles that each map to different capabilities. A mechanism is needed for the client to inform the issuer which role to use when determining which capabilities are allowed. It is related to pr #5, so if a client asks for all capabilities allowed then the issuer would return different answers depending on which role is asked for.

DrDaveD commented 3 years ago

During discussion at today's meeting an objection was raised that the proposed mechanism of using the wlcg.groups scope has been defined for a different purpose, and this use may not be compatible with that. Perhaps a new scope should be defined instead.

msalle commented 3 years ago

Would it work to just also return the wlcg.groups as claim with its correct values in the token? I think that could be just ok with the current document...? The reason it should not be ignored is for interoperability reasons: different services/sites might need different parts of the tokens, some will consume the capabilities, others the groups/roles type information. I think that the wlcg.groups* is essentially the right scope to request.

maarten-litmaath commented 3 years ago

The use case makes sense to me. We first need to check what IAM can do today and then decide what would be the best way to deal with missing functionality that is crucial for this matter.

hannahshort commented 3 years ago

To me the crux of the matter is: we assumed that clients would always know which capabilities to request, would only request the necessary ones and the token request should fail if they were not entitled to them. This doesn't seem to be the case for this PR or PR5.

It seems like what you really need is a way to bring back all relevant entitlements for a user that the client is allowed to receive, which can also be scoped by "role" (i.e. admin vs prod). This would be used by clients that don't have enough information to make specific scope requests.

This seems to be a valid use case in the case of Vault (and probably others where you only know the role of the user) but I'd prefer that we solve the larger issue.

DrDaveD commented 3 years ago

Copying comments from #5 that are more relevant here:

@paulmillar wrote:

If I've understood correctly (not guaranteed!) it might be helpful to rephrase this somewhat.

The user (or some agent acting on behalf thereof) wishes to have tokens to perform some activity. That might be "analysis", "production", "skimming" or whatever labels we might wish to give those actions. Based on those labels, the OP issues a token that authorises some subset of all possible activities for that user. That is to say, the token could authorise everything the user is entitled to do, or it might be some proper subset.

It seems to me (perhaps wrongly) that the problem here is that the same language is being used both to describe the desired activity of the user (or agent thereof) and also what operations are allowed. For those within the VOMS world, this has become natural. We talk about Role=production as voms-proxy credentials used within certain work-flows. However, as Maarten points out, it does lead to duplication of knowledge: you have to know what to ask for.

Perhaps this problem might be easier to understand (and perhaps handled better) if we had a clearer separation between these two concepts. For example, the request could come with desired scopes defined using one language (e.g., work-flow:production), but the OP resolves these into scopes that the individual services then understand.

@andreaceccanti wrote:

The problem IIUC is that you are trying to use the capabilities for a use case where identity and group-based authorization would work better (like access to home directories).

The profile already defines a mechanism to request conditional group membership, so that users can choose the "hat" they want to wear for a particular session. This decision cannot be delegated to a component in the middle of chain (i.e. vault), but must be part of the user request (as it is done today with VOMS and explicit role selection).

We want to use only capability-based scopes in the access tokens, so the services only need to understand one method. We just need a method of communicating a request from the client to the token issuer/OP to convert the group/role into the appropriate capabilities for that group/role. We can use a different scope than wlcg.groups if that's what is agreed. Any scope will do, but it does need to be a scope because that's what the libraries make easiest to pass through from the client to the token issuer. (I wouldn't choose "work-flow" but perhaps "role" or "wlcg.role"). 

DrDaveD commented 3 years ago

Some of us (particularly @andreaceccanti, @paulmillar, @hannahshort, and I) discussed this after the regular meeting timeslot today and we got an agreement for standardizing this as long as we chose a different name for it than wlcg.groups. We preferred it to be a different name than had been used before, in order to avoid confusion. The name we settled on was "wlcg.capabilityset" to request the set of capabilities for the given role. For example it would be "wlcg.capabilityset:production". The name of the capability set and the list of capabilities to return is entirely up to the token issuer.

@jbasney please update the PR to use that name.

In addition, Andrea favored also using this mechanism to satisfy the goal of PR #5; instead of requesting individual scopes with superpaths such as "/*", just request "wlcg.capabilityset:analysis" for example. Another good example could be "wlcg.capabilityset:default".

maarten-litmaath commented 3 years ago

Looks good!

DrDaveD commented 3 years ago

Oh it occurs to me we should probably also define what happens if someone specifically requests one of the capabilities that are in the set. In that case I assume it would reduce that capability from the set. The wlcg.capabilityset could be processed first, and after that the normal rules are applied.

And another thought: what if someone wanted to delete one of the capabilities?

maarten-litmaath commented 3 years ago

Prefix unwanted capabilities with a minus sign?

hannahshort commented 3 years ago

We thought about something similar for groups, i.e. requesting all default groups and some optional ones, what happens with duplicates? All we put was The returned wlcg.groups claim will not contain duplicates

andreaceccanti commented 3 years ago

Oh it occurs to me we should probably also define what happens if someone specifically requests one of the capabilities that are in the set. In that case I assume it would reduce that capability from the set. The wlcg.capabilityset could be processed first, and after that the normal rules are applied.

Yes, this would not be too difficult to implement.

And another thought: what if someone wanted to delete one of the capabilities?

Prefix unwanted capabilities with a minus sign?

This filtering capability should not be supported (at least not overloading the oauth scope request). If clients client want to go granular then they should explicitly request each capability with the respective scope.

Also note that a client could first request a capability set, inspect the result (issued scopes are returned in the token response) and then reduce the scopes with whatever needed granularity.

Or the server could also implement an oauth protected API to expose what capabilities would be returned for a capabilityset for a user, so that a client application could first call to this API to know how to make fine grained capability requests (this would avoid issuing tokens in the token server).

DrDaveD commented 3 years ago

I am limited in what the vault client can reasonably do because I'm depending on open source plugins maintained by other people and used by multiple projects. At a minimum a new-nonstandard API (the last suggestion) would not be good.

Vault's current supported workflows are:

  1. Request one of a number of pre-configured static sets of scopes in an initial request (which returns an id_token, refresh_token, and access_token). Each static scopes set is identified by a "role". The refresh_token is stored in a given "secrets" path in vault.
  2. Request a new access_token using a stored refresh_token (which typically also returns an updated refresh_token with the same set of scopes that replaces the old one).
  3. Do RFC8693 token exchange of one access_token for another (based on a PR I made that is not yet merged).

The tricky use case with this set of supported workflows is one where we might want to store a special reduced-capability refresh_token in a place accessible by some sort of "robot" or cron job. I can see where someone might want that for security purposes, to not give the robot the full set of capabilities. I was thinking that we could do this through workflow #1 with a special role for that purpose, but with our wlcg.capabilityset proposal there won't be a way to get that reduced set into a refresh_token.

There are other possible way to implement this use case, but I'm not sure it's a good idea to design it in so that we can't do it the most straightforward way, in the original request. Note that I'm not sure that the use case is something that people will really want to do.

jbasney commented 3 years ago

I don't want to be a bottleneck on group edits to this new section being drafted. I enabled "edits by maintainers" so people with write access to WLCG-AuthZ-WG/common-jwt-profile can add new commits to the jbasney:group-based-capability-selection branch to help with the drafting/editing, though I'm not sure who that includes. Alternatively, could we merge this into a WLCG-AuthZ-WG branch so everyone can contribute directly?

DrDaveD commented 3 years ago

Yes, a branch would allow us to submit PRs against the branch.

hannahshort commented 3 years ago

Merging into a new branch :)

DrDaveD commented 3 years ago

And another thought: what if someone wanted to delete one of the capabilities?

Prefix unwanted capabilities with a minus sign?

This filtering capability should not be supported (at least not overloading the oauth scope request). If clients client want to go granular then they should explicitly request each capability with the respective scope.

In a discussion about this with Jim Basney today he had an idea that would take care of this. If we wanted a reduced set of capabilities in a refresh token, we could always just define a new capabilityset with the token issuer. For example if we wanted a set of capabilities for a production robot refresh token with no storage.write, we could always define a wlcg.capabilityset:/dune/productionreadonly in addition to wlcg.capabilityset:/dune/production. So we're good with this plan.

This is reflected in PR #10 against this branch. Please move followup discussion there.