Home > Software engineering >  How to manage and implement business context roles in resource server with OPENID? (SpringBoot and K
How to manage and implement business context roles in resource server with OPENID? (SpringBoot and K

Time:10-16

I am currently implementing a system with Spring Boot as a resource server and Keycloak as an authorization server. I want to be able to create different roles for users but that should happen automatically, so no admin that is managing the Keycloak backend for this matter.

I have read about the Keycloak admin api but I am very unsure about this approach because it would mean storing login credentials in my resource server, which is not best practice at all, I think. So I want to store (business context) roles in the resource server but without implementing my own authorization approach for each endpoint. I want to be able to use the roles stored in the resource server with spring security for instance the hasRole() method but also all the other functionallity that is already being used by the OPEN ID authenication that hands over the roles from the authorization server.

So how would I go about this? Is there maybe a secure way to update user roles in the authorization server from the resource server? That would seem unlikely to me because (though not in my case) the authorization server might not be owned by the resource owner.

Or as another approach is it possible to fuse the roles handed to Spring Boot from keycloak and the roles stored in the resource server database and look them up after authenticating a user via keycloak?

I would be very glad about some code snippets but pointers to tutorials explaining the topic are just as appreciated.

CodePudding user response:

Sooooo, after some more googleing I found the perfect solution. There is documentation here: https://www.baeldung.com/spring-security-map-authorities-jwt but I actually could not make a lot of that.

There was an answer on stackoverflow Spring Security: mapping OAuth2 claims with roles to secure Resource Server endpoints that worked really well. But also there is my source code.

private class CustomJwtAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {

    @Override
    public AbstractAuthenticationToken convert(final Jwt source) {

        Collection<GrantedAuthority> authorities = new ArrayList<>();
        authorities.add(new SimpleGrantedAuthority("ROLE_user"));

        // Get a unique id from jwt and search for the user in your database
        String suid = "";
        if(source.hasClaim("sub"))
            suid = source.getClaim("sub");
        else
            return new JwtAuthenticationToken(source, authorities);

        Person person = personRepository.findBySuid(suid);
        
        // Set any role you want, I have not tried but I am sure it also works 
        // with permissions 
        if ( person != null && person.getRole().equals("admin") )
            authorities.add(new SimpleGrantedAuthority("ROLE_admin"));
        else
            return new JwtAuthenticationToken(source, authorities);

        return new JwtAuthenticationToken(source, authorities);
    }
}

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception{
    http     // Remove h2 setup for production
            .authorizeRequests().antMatchers("/h2-console/**").permitAll()
            .and().csrf().ignoringAntMatchers("/h2-console/**")
            .and().headers().frameOptions().sameOrigin()

            // Above is only for h2 console
            .and().cors().and().csrf().disable()
            .authorizeRequests()
            .antMatchers("/api/heroes/**").hasRole("admin")       
            .anyRequest().authenticated()
            .and()
            .oauth2ResourceServer()
            .jwt()
            .jwtAuthenticationConverter(new CustomJwtAuthenticationConverter());
    return http.build();
}

You can compute the roles you want for the authenticated user in any way you want for instance work with JPA or another datasource where your user data is stored. The entire JWT token is handed to you and as you can see I am using the "sub"-key but with varying implementations of the authorization server you should choose a unique identifier yourself.

After all I went for the resource server persistent role management. I think it is good practice because the business context should be stored there. I hope I could help everyone who has the same question.

CodePudding user response:

I don't quite agree with you: authorization-server is, in my opinion, the best place to manage authentication and authorization.

I prefer to provide authorization server with "plug-ins" to enrich access tokens with the private claims I need in spring-security.

In the ends, this leads to define a custom authentication converter too, but DB access (to retrieve roles or whatever) occurs only once per access token generation (by authorization-server to add it to access-token) instead of once per request (by resource server to build security context).

  • Related