A while back, I created a spring book application that uses Okta as the OIDC provider. It's based on a previous version of this great example by Matt Raible.
What I'm trying to do now is obtain the group or roles for the logged in user.
Matt responded to Spring Boot / Okta - how to retrieve the users groups saying I should add claims to Okta interface, which I've done.
He qualified it, saying
Then, if you're using the Okta Spring Boot starter, your groups will automatically be converted to Spring Security authorities.
I converted to Spring Boot starter kit, with the following steps:
Added the starter kit to pom.xml
changed application properties to use variables from the starter kit as follows:
#okta spring boot starter kit
okta.oauth2.issuer=https://dev-xxxx.okta.com/oauth2/default
okta.oauth2.audience=api://default
okta.oauth2.groupsClaim=groups
#okta spring boot starter kit test
okta.oauth2.clientId=xxxxxxxxxxxx
okta.oauth2.clientSecret=xxxxxxxxxxxxxxxxx
And it works as it did before.
However, when I print the Authorities, I still don't see the groups. The only authority I see is this:
Authority: ROLE_USER Username: xxxxx@gmail.com
Heres the code which prints the authorities:
Collection<? extends GrantedAuthority> authorities = principal.getAuthorities();
for (GrantedAuthority authority : authorities) {
System.out.println("Authority: " + authority.getAuthority());
}
Is there anything I'm doing wrong here?
I thought that adding the groupsClaim
property would automatically map values to authorities. Are you sure you're adding it to the ID token?
The following technique is what we use in JHipster, which uses Spring Security w/o the Okta starter.
Add a GrantedAuthoritiesMapper
bean in your security configuration.
/**
* Map authorities from "groups" or "roles" claim in ID Token.
*
* @return a {@link GrantedAuthoritiesMapper} that maps groups from
* the IdP to Spring Security Authorities.
*/
@Bean
public GrantedAuthoritiesMapper userAuthoritiesMapper() {
return (authorities) -> {
Set<GrantedAuthority> mappedAuthorities = new HashSet<>();
authorities.forEach(authority -> {
// Check for OidcUserAuthority because Spring Security 5.2 returns
// each scope as a GrantedAuthority, which we don't care about.
if (authority instanceof OidcUserAuthority) {
OidcUserAuthority oidcUserAuthority = (OidcUserAuthority) authority;
mappedAuthorities.addAll(SecurityUtils.extractAuthorityFromClaims(oidcUserAuthority.getUserInfo().getClaims()));
}
});
return mappedAuthorities;
};
}
The relevant methods from SecurityUtils
are:
public static List<GrantedAuthority> extractAuthorityFromClaims(Map<String, Object> claims) {
return mapRolesToGrantedAuthorities(getRolesFromClaims(claims));
}
@SuppressWarnings("unchecked")
private static Collection<String> getRolesFromClaims(Map<String, Object> claims) {
return (Collection<String>) claims.getOrDefault("groups",
claims.getOrDefault("roles",
claims.getOrDefault(CLAIMS_NAMESPACE + "roles", new ArrayList<>())));
}
private static List<GrantedAuthority> mapRolesToGrantedAuthorities(Collection<String> roles) {
return roles.stream()
.filter(role -> role.startsWith("ROLE_"))
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
}