The default authentication managers / builders for OAuth2 builds authorities from "scope" claim.

IMO, this comes with serious limitations, main reason being it mixes the notions of * authorization: what the user is allowed to on a resource server * scope: what the user allows a client to do in his name with resource-servers

If authorisation-server serves authorities, shouldn't we expect user authorities on resource-servers to be a scoped view (I mean a sub-set) of all user's authorities?

To stick to spring-security OAuth2 boot samples, I'd expect "message" to be a scope and ["message:read", "message:write"] to be authorities within this scope ("message:" prefix used as a way to prevent collisions with authorities from other scopes).

To illustrate the confusion, still looking at the samples, subject user is granted with "ROLE_USER" authority only on the authorisation-server and then either "message:read", "message:write" or "none" (but never "ROLE_USER") on resource servers, depending on wich client grants where used. Isn't it wired?

Of course it's not so complicated to switch the AuthenticationManager on JwtConfigurer or OpaqueTokenConfigurer to something aware of "authorities".

Question are: * why using "scope" to grant OAuth2 authorities in default resource-server AuthenticationManager? specifically when default authorization-server user token converter puts it in "authorities" claim (even SubjectAttributeUserTokenConverter from samples does so...) * is it planed to register "public" claims for Spring authorities as specified by JWT RFC or even introspection one? Private claims work just fine thougt. * is it planed to add some flexibility to granted-authorities retrieving from token ?

Regarding last point, it is currently easier to do for JWT than opaque tokens introspection as it is possible to configure a Converter<Jwt, Collection<GrantedAuthority> when for introspection, the all AuthenticationManager has to be configured.

As a decoded token is no more than a claim-set, in both cases, the ability to configure a Converter<Map<String, Object>, Collection<GrantedAuthority>> (or even Converter<Map<String, Object>, Authentication> for more flexibility) might be just enough.

Comment From: jzheaux

@ch4mpy these are good questions - do you have a proposal? I think this would help me focus my thoughts. The title of the ticket is "More flexibility on authorizations lookup", though it already seems to me to be quite flexible. In what way are you feeling encumbered?

What you've called out is one of the motives for why granted authorities derived from the scope claim are prefixed with "SCOPE_". They are, indeed, not a "ROLE_". Generally speaking though, both of these become granted authorities so that they can both be used in security expressions.

Comment From: ch4mpy

@jzheaux the ticket title might be too restrictive. Maybe some default behaviors could be questioned too.

An easy way to get things easier for introspected tokens: make claims to authorities converter configurable as it is for JWT. Replacing authentication manager should be the way to go for those wishing to switch Authentication implementation, not just how authorities set is built.

Regarding scopes and authorities, I see * authorities as a contract between resource-server and end-user (what user is allowed to) * scope as a contract between client and end-user (what user agrees to let this client do on his behalf).

If we consider multi-service applications, we can expect OAuth2 authorization scopes to determine what services and functionalities a client is restricted to: with all possible scopes, the client would be granted with all end-user authorities and the narrower the scopes the less the client can do compared to what the end-user is allowed to.

From that perspective, scopes should be used to filter authorities (even deny authentication), not "add to" or "set" it. Some ways this could be implemented in default OAuth2 authentication manager: * use authorities claim as default instead of scope and scp to build authorities set * add a configurable requiredScopes property to default authentication manager: even if authorities are embedded in token claims, some resource-servers might completely deny authentication to client presenting authorizations without expected scopes (end-user has authorities to access the service but did not grant the scope to this particular client)

Comment From: jzheaux

Ah, gotcha. Thanks for the clarification.

Yes, the API for opaque tokens is still very simple. I think that #6830 will address some of what you are referring to.

Are you aware of any specifications that outline the authorities claim and what its semantics are? It's easier for the code to offer an implementation opinion when it can point to an RFC as its reason.

I don't think we ought to change what defaults Spring Security uses today. Resource Server interprets the bearer token as the client's authority to operate on behalf of a resource owner, it doesn't represent the resource owner itself. If it's clear, though, that authorities means the "owner's authorities", then there may be some merit in Spring Security pulling those out and adding them as ROLEs alongside the SCOPEs that it already extracts.

Comment From: ch4mpy

Resource Server interprets the bearer token as the client's authority to operate on behalf of a resource owner, it doesn't represent the resource owner itself

@jzheaux, I completely agree with that, this is not my point here. What I ask is: shouldn't we use scope claim to provide with a "scoped" view of authorities (that is a subset) instead of setting or adding to it?

As I understand OAuth: * same client, same end-user, different scopes => different authorities * same end-user, same scopes, different clients => same authorities * same client, same scopes, different users => different authorities

The authorities clients are granted with being the union of the end-user authorities subsets provided by each scope.

Now, regarding GrantedAuthority, it is something of spring-security. What is in RFCs to address such are "private" claims. So you, as framework team, are free to choose the semantic. Using authorities claim containing a JSON array as done for instance in SubjectAtributeUserTokenConverter of spring-security-samples-boot-oauth2authorizationserver seems a good way to go. It's surprising that resource-server samples just ignore it.

Considering the popularity of spring-security, maybe would it be possible to register a public key for authorities. If the name is not too specific, it could also be used by other framework expecting permissions to be embedded in token claims.

Comment From: ch4mpy

Reading the OAuth2 RFC again, here is what I found outstanding about scopes:

  1. Introduction [... detailing password authentication drawbacks ...] o Third-party applications gain overly broad access to the resource owner's protected resources, leaving resource owners without any ability to restrict duration or access to a limited subset of resources. [...]

3.3. Access Token Scope [...]If the value contains multiple space-delimited strings, their order does not matter, and each string adds an additional access range to the requested scope.[...]

10.3. Access Tokens [...] The authorization server SHOULD take the client identity into account when choosing how to honor the requested scope and MAY issue an access token with less rights than requested. [...]

All those quotes indicate "scope" define client access as subset of resource-owner one.

Last statement could be interpreted as OAuth2 scopes <=> spring-security GrantedAuthority but, 1. the spec is so precise that if they intended scope and resource-owner access to be the same thing, they'd use a single wording 2. OAuth2 primary goal is provinding an alternative to password authorization, that is providing with identity, not necessarly providing with all necessary for resource access decision

Actually, In most use-cases, I prefer authorities not to be contained in token claims: * let's consider resource-server for which the authorization server is external to the team. When evolving access management on the resource-server, how to have access rights definitions evolve on authorization server too ? and on client(s) ? * now consider a large scale multi-service with complicated access management (mean a lot of different possible authorities). Embedding authorities directly in token claims would result in potentially really fat tokens and will require dedicated strategies to avoid authorities names clash between resource-servers (what if a client is granted with ROLE_ADMIN for a minor service and wire-transfer service also has an ADMIN role?) * exposes too much of resource-servers security implementation details (exact names of each and every GrantedAuthority is public) * how to ensure scopes names can be concealed (conventions, clashes, etc.) if different resource-servers use different technologies (not all spring-security)?

So there are good reasons to de-couple scopes and my authorities and my favorite solution is to have: * the authorization-server provide with identity and scopes * each resource-server to manage on its own authorities and how it is wired to identity retrieved from OAuth2 token (or username/password or whatever)

To fit OAuth2 specs, such a solution where resource-server loads resource-owner authorities from an external source, requires a step where retrieved authorities are "scoped", that is authentication only contains authorities mapped to both resource-owner and token scopes.

Comment From: jzheaux

You make some great points about why resource servers may not want to tightly couple the values in the scope claim directly to GrantedAuthority identifiers.

each resource-server to manage on its own authorities

How would you propose Spring Security's API change to make this simpler? To me, it already seems very simple:

http
    .oauth2ResourceServer()
        .jwt()
            .jwtAuthenticationConverter(myConverter())

Because this contract is Jwt -> Authentication, the application can use any aspect of the original bearer token to derive authorities in whatever application-specific way required. As stated earlier, #6830 will address the same for opaque token.

use authorities claim as default instead of scope and scp to build authorities set

Since authorities is not yet listed in any RFC, it would likely be too strong of an assumption to use authorities as the default. That said, it may be reasonable to add something like JwtGrantedAuthoritiesConverter#setAuthoritiesClaimName. It doesn't really seem to me that this addresses your concern of wanting a Resource Server to manage its own authorities, though.

some resource-servers might completely deny authentication to client presenting authorizations without expected scopes

It doesn't yet seem apparent that such an approach is common enough, especially when it's so easy to do it with the existing API:

http
    .authorizeRequests()
        .anyRequest().hasAuthority("SCOPE_requiredScope");

However, if the application needed to treat a missing scope as a failed authentication instead of as an access denial, then it could throw an AuthenticationException from a custom JwtAuthenticationConverter.

Comment From: ch4mpy

I believe my messages are too long. I tried to summarize my thoughts about scopes here. Hope you have time to read it.

Since authorities is not yet listed in any RFC, it would likely be too strong of an assumption to use authorities as the default

  1. better use different claims for different things (authorities and scopes are definitely different concepts)
  2. scope claim isn't in the JWT RFC neither. It is in introspection claim and OAuth2 sucess-response but this one is known to the client only and there's no way in RFC-6749 for resource server to validate scopes a client would claim
  3. spring-security might be widely enough adopted to request for an authorities "public" claim name

it may be reasonable to add something like JwtGrantedAuthoritiesConverter#setAuthoritiesClaimName. It doesn't really seem to me that this addresses your concern of wanting a Resource Server to manage its own authorities, though

True and true. It would be nice to enable claim name configuration for applications opting for claim embedded authorities.

For those (like me) preferring resource-servers retrieve authorities from external source, I can't think of something easier than providing with a custom the authorities converter. JWT configuration is already simple enough and introspection one would be if #6830 was solved.

.hasAuthority("SCOPE_requiredScope")

This requires scopes where turned into authorities which should not happen. In the end, I'm not sure authentication needs to check scopes: * if authorization-server provides with an authorities claim, it should only contained "scoped" authorities (no authorities if not the right scopes) * if authorities are retrieved from an external source by the resource-server authorities converter, it should also apply "scope" filter (and retain nothing if not the right scopes)

Comment From: jzheaux

scope claim isn't in the JWT RFC neither

Hmm, that is a good point.

I believe that I've understood from you that you prefer to have both claims, scope and authorities in your token. Since that would mean you'd want to have JwtGrantedAuthoritiesConverter target a claim, I think that adding a setter is a sensible way to do this, e.g JwtGrantedAuthoritiesConverter#setAuthoritiesClaimName

If you're agreed, would you be interested in adding a PR to add the setter?

Comment From: ch4mpy

Few questions:

  • what should be the default authorities claim name ? I anticipate you'd prefer it to be ["scope", "scp"] for backward compatibility reasons, but I believe authorities would be a much better default (again scopes and permissions are not the same things)
  • what about the prefix? should a setter be exposed for it too? should it automatically be SCOPE_ for scope and scp claims and empty string otherwise?
  • do you intend to duplicate code for introspection?

Comment From: jzheaux

  • Yes, let's leave it as ["scope", "scp"] for the default
  • Yes, let's add setAuthorityPrefix, defaulting to SCOPE_
  • I'm still mulling this over, but yes I'm thinking that the code will be duplicated for introspection (not in this PR, though)

To track these two separate features, please use #7100 and #7101 as the fixes issue for the two commits.

Comment From: jzheaux

I'm closing this ticket in favor of #7100 and #7101