Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

spring-security-oauth2 JwkTokenStore with custom user details service

I've a rich web (react based) front end application that sends request to a backend ResourceServer application. The requests are sent with JWT in the header for authentication. My setup does authentication against an Okta Authorization Server and retrieves Groups/Authorities from a separate service.

I have the backend server setup as a Springboot app with Spring Security Oauth2 resource server @EnableResourceServer

@Configuration
@EnableResourceServer
public class SecurityConfig extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception{
        http.requestMatchers().antMatchers("/api/**")
                .and()
                .authorizeRequests().anyRequest().authenticated();
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.tokenServices(tokenServices()).resourceId("my-okta-resource-server-client-id");
    }

    @Bean
    @Primary
    public DefaultTokenServices tokenServices() throws Exception {
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setTokenStore(tokenStore());
        return tokenServices;
    }

    @Bean
    public TokenStore tokenStore() throws Exception {
       return new JwkTokenStore("https://my-org.oktapreview.com/oauth2/my-auth-server/v1/keys");
    }

With this setup I'm able to validate the JWT token alright using the JwkTokenStore implementation (It internally uses JwkVerifyingJwtAccessTokenConverter to verify the Json Web Keyset from the keys uri supplied). However I'm unable to configure it to either

  1. Pull out specific JWT claims for it to create the Authentication. It only looks at the user_name claim, I'd like to use email or other claims on there.
  2. Inject a custom User Details service. The DefaultUserAuthenticationConverter which encapsulates a UserDetailsService is embedded deep within. This I need to lookup authorities the user is setup with in a separate rest service.

Spring-security-oauth2's JWK related implementation seems closed with most of the JWKs verification related classes not being public. What's a good way to utilize the token verification provided by JwkTokenStore and also be able to have your own user details in there to load authorities.

Software versions in use: Springboot 1.5.2.RELEASE, spring-security-core:4.2.3, spring-security-oauth2:2.0.14,

like image 982
Amit Kapoor Avatar asked Jul 06 '17 21:07

Amit Kapoor


1 Answers

You should use the so called DefaultAccessTokenConverter to extract these extra claims and add it to an OAuth2Authentication object.

You can @Autowire the CustomAccessTokenConverter into your ResourceServerConfiguration class and then set it to your JwtTokenStore() configuration.

ResourceServerConfiguration:

@Autowired
private CustomAccessTokenConverter yourCustomAccessTokenConverter;

@Override
public void configure(final ResourceServerSecurityConfigurer config) {
    config.tokenServices(tokenServices()).resourceId(resourceId);
}

@Bean
@Primary
public DefaultTokenServices tokenServices() {
    final DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
    defaultTokenServices.setTokenStore(jwkTokenStore());
    return defaultTokenServices;
}

@Bean
public TokenStore jwtTokenStore() throws Exception {
   return new JwkTokenStore("https://my-org.oktapreview.com/oauth2/my-auth-server/v1/keys", accessTokenConverter());
}

@Bean
public JwtAccessTokenConverter accessTokenConverter() {
    JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
    converter.setAccessTokenConverter(yourCustomAccessTokenConverter);
    return converter;
}

The CustomAccessTokenConverter can be configured, so that the custom claims get extracted here.

CustomAccessTokenConverter:

@Component
public class CustomAccessTokenConverter extends DefaultAccessTokenConverter {

    @Override
    public OAuth2Authentication extractAuthentication(Map<String, ?> claims) {
        OAuth2Authentication authentication = super.extractAuthentication(claims);
        authentication.setDetails(claims);
        return authentication;
    }
}

Afterwards you should be able to access the claims via the OAuth2Authentication object.

like image 116
git-flo Avatar answered Oct 17 '22 02:10

git-flo