spring-projects / spring-projects/spring-security
Extendable JWT authority mapping
@jzheaux is already working on this.
Since Jul 17, 2020.
- Dominant language
- Java
- Stars
- 9.6k
- Forks
- 6.3k
- Avg merge
- 2d 11h
- Merged PRs (30d)
- 52
Description
Expected Behavior
There seems to be no standard on what JWT contains to communicate client authorities. At the moment Spring Security just maps everything within scope or scp to SCOPE_. I think, it would be nice to have the possibility to configure further mappings, to be used in conjunction with pathMatchers.
Current Behavior
As far as I could see, there is no way to extend the default in an easy way.
Context
I personally use the following:
public final class ExtendableReactiveJwtAuthenticationConverterAdapter implements Converter<Jwt, Mono<AbstractAuthenticationToken>> {
private HashMap<String, String> authoritiesMap = new HashMap<>() {{
put("scope", "SCOPE_");
put("scp", "SCOPE_");
}};
@Override
public Mono<AbstractAuthenticationToken> convert(Jwt jwt) {
return Flux
.fromIterable(authoritiesMap.entrySet())
.filter(claimPrefixEntry -> jwt.containsClaim(claimPrefixEntry.getKey()))
.flatMap(entry -> extractAuthorities(jwt, entry.getKey(), entry.getValue()))
.collectList()
.map(authorities -> new JwtAuthenticationToken(jwt, authorities));
}
public ExtendableReactiveJwtAuthenticationConverterAdapter addMapping(String claimName, String prefix) {
authoritiesMap.put(claimName, prefix);
return this;
}
public ExtendableReactiveJwtAuthenticationConverterAdapter setMappings(HashMap<String, String> authoritiesMap) {
this.authoritiesMap = authoritiesMap;
return this;
}
@SuppressWarnings("unchecked")
private Flux<GrantedAuthority> extractAuthorities(Jwt jwt, String claimName, String prefix) {
Object claim = jwt.getClaim(claimName);
Flux<String> authorities;
if (claim instanceof String)
authorities = Flux.fromArray(((String) claim).split(" "));
else if (claim instanceof Collection)
authorities = Flux.fromIterable((Collection<String>) claim);
else
authorities = Flux.empty();
return authorities.map(authority -> new SimpleGrantedAuthority(prefix + authority));
}
}
This allows me to configure my OAUTH resource server like:
.jwt(jwtSpec -> jwtSpec.jwtAuthenticationConverter(
new ExtendableReactiveJwtAuthenticationConverterAdapter().addMapping("groups", "GROUP_")
))
Like so, defaults are preserved and extensions are easy to configure. Plus, I think it is a bit more readable than JwtGrantedAuthoritiesConverter.
At the moment the default uses ReactiveJwtAuthenticationConverterAdapter with a hard coded dependency on JwtAuthenticationConverter which again hard coded depends on JwtGrantedAuthoritiesConverter. This chain is a little inflexible. I understand, it is due to reusing the same code for blocking and reactive implementation. However, it would (probably not just for me) be nice to have the same functionality as within ExtendableReactiveJwtAuthenticationConverterAdapter.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.