Home > Mobile >  OAuth2 GrantedAuthorities not present in JWT when using custom UserDetailsService
OAuth2 GrantedAuthorities not present in JWT when using custom UserDetailsService

Time:04-09

Using the new spring-authorization-server 0.2.3 and following https://github.com/spring-projects/spring-authorization-server/tree/main/samples as reference I was able to setup an authorization server, resource server and a client successfully when using an InMemoryUserDetailsManager as follows


@EnableWebSecurity
public class DefaultSecurityConfig {

    @Bean
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        return http
                .authorizeRequests(authorizeRequests ->
                        authorizeRequests.anyRequest().authenticated()
                )
                .formLogin(Customizer.withDefaults())
                .build();
    }


    @Bean
    UserDetailsService users() {
        User.UserBuilder users = User.withDefaultPasswordEncoder();
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(users.username("user1").password("password").roles("USER").build());
        manager.createUser(users.username("admin").password("password").roles("USER", "ADMIN").authorities("r1","r2","r3").build());
        return manager;
    }

}

This works well, In the client, I can see the authorities Granted Authorities=["r1","r2","r3"] present.

Now when I attempt to implement my own UserDetailsService which retrieves users from a Mongo Database, I stop seeing the GrantedAuthorities being passed to the client and only see Granted Authorities=[ROLE_USER, SCOPE_openid]

This is what I now have in the DefaultSecurityConfig


@EnableWebSecurity
public class DefaultSecurityConfig {

    @Bean
    SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
        return http
                .authorizeRequests(authorizeRequests ->
                        authorizeRequests.anyRequest().authenticated()
                )
                .formLogin(Customizer.withDefaults())
                .build();
    }

    @Autowired
    private MongoTemplate mongoTemplate;

    @Bean
    UserDetailsService users() {
        return new CustomUserDetailsService(mongoTemplate);
    }
}

And my CustomUserDetailsService looks like the following:


public class CustomUserDetailsService implements UserDetailsService {

    private final MongoTemplate mongoTemplate;

    public CustomUserDetailsService(MongoTemplate mongoTemplate) {
        this.mongoTemplate = mongoTemplate;
    }

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Criteria criteria = Criteria.where("email").is(username);
        CustomUser user = mongoTemplate.findOne(new Query(criteria), CustomUser.class, "vOAuthUser");
        if (user != null) {
            log.info("Found user {}", user.email());
            List<GrantedAuthority> authorities = getUserAuthority(user.groups());
            return buildUserForAuthentication(user, authorities);
        } else {
            throw new UsernameNotFoundException("username not found");
        }
    }

    private UserDetails buildUserForAuthentication(CustomUser user, List<GrantedAuthority> authorities) {
        return new org.springframework.security.core.userdetails.User(user.email(), user.password(), authorities);
    }

    private List<GrantedAuthority> getUserAuthority(Set<String> groups) {
        List<GrantedAuthority> authorities = new ArrayList<>();
        groups.forEach(s -> {
            Criteria criteria = Criteria.where("name").is(s);
            CustomRole role = mongoTemplate.findOne(new Query(criteria), CustomRole.class, "vRole");
            if (role != null) {
                authorities.addAll(role.grantedAuthorities());
            }
        });
        return authorities;
    }

}


Any help is greatly appreciated.

CodePudding user response:

Have you defined a OAuth2TokenCustomizer bean in your security configuration? You can add Granted Authorities there if you need, like in the following code:

   @Bean
   @SuppressWarnings("unused")
   OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer() {
      return context -> {
         JoseHeader.Builder headers = context.getHeaders();
         JwtClaimsSet.Builder claims = context.getClaims();

         Authentication principal = context.getPrincipal();
         Set<String> authorities = principal.getAuthorities().stream()
               .map(GrantedAuthority::getAuthority)
               .collect(Collectors.toSet());
         claims.claim("authorities", authorities);
      };
   }
  • Related