7

I am attempting to build a Spring application that uses JWT tokens and the OAuth2 protocol. I have the Authentication Server running thanks to this tutorial. However, I am struggling with getting the Resource Server to function properly. From following the article, and thanks to a response to a prior question, this is my current attempt:

Security config for Resource Server:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Value("${security.signing-key}")
    private String signingKey;

    @Value("${security.encoding-strength}")
    private Integer clientID;

    @Value("${security.security-realm}")
    private String securityRealm;

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setVerifierKey(signingKey);
        return converter;
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean ResourceServerTokenServices tokenService() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setSupportRefreshToken(true);
        return defaultTokenServices;
    }
    @Override
    public AuthenticationManager authenticationManager() throws Exception {
        OAuth2AuthenticationManager authManager = new OAuth2AuthenticationManager();
        authManager.setTokenServices(tokenService());
        return authManager;
    }

}

Resource Server config:

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
    @Autowired
    private ResourceServerTokenServices tokenServices;

@Value("${security.jwt.resource-ids}")
private String resourceIds;

@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
    resources.resourceId(resourceIds).tokenServices(tokenServices);
}

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

}

Security config for Authorization Server (from noted tutorial):

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Value("${security.signing-key}")
    private String signingKey;

    @Value("${security.encoding-strength}")
    private Integer encodingStrength;

    @Value("${security.security-realm}")
    private String securityRealm;

    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    @Override
    protected AuthenticationManager authenticationManager() throws Exception {
        return super.authenticationManager();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService)
                .passwordEncoder(new ShaPasswordEncoder(encodingStrength));
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .httpBasic()
                .realmName(securityRealm)
                .and()
                .csrf()
                .disable();

    }

    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
        converter.setSigningKey(signingKey);
        return converter;
    }

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }

    @Bean
    @Primary //Making this primary to avoid any accidental duplication with another token service instance of the same name
    public DefaultTokenServices tokenServices() {
        DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
        defaultTokenServices.setTokenStore(tokenStore());
        defaultTokenServices.setSupportRefreshToken(true);
        return defaultTokenServices;
    }
}

Now, when I attempt to make a request to the Resource server, I receive an error such as follows:

{"error":"invalid_token","error_description":"Invalid access token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsidGVzdGp3dHJlc291cmNlaWQiXSwidXNlcl9uYW1lIjoiam9obi5kb2UiLCJzY29wZSI6WyJyZWFkIiwid3JpdGUiXSwiZXh wIjoxNTE1MTE3NTU4LCJhdXRob3JpdGllcyI6WyJTVEFOREFSRF"}

I have a couple questions:

  • To my understanding, the issue is often associated with token stores. How does one handle separting the servers when a JwtTokenStore is used?
  • Second, currently, my Resource app relies on having access to a key. To my understanding of the JWT and 0Auth specs, this should not be necessary. Rather, I should be able to delegate the validation to the Authentication server itself. From the Spring docs, I thought the following property might be applicable security.oauth2.resource.token-info-uri=http://localhost:8080/oauth/check_token. However, if I attempt to not rely on a key with my Resource Server, then I run into difficulties setting my ResourceServerTokenService. The service expects a token store, the JWTTokenStore uses a JwtAccessTokenConverter, and the converter uses a key (removing the key resulted in the same invalid token error I experienced earlier).

I am really struggling to find articles that show how to separate the Auth and Resource server. Any advice would be appreciated.

EDIT: Actually, the code for the Resource Server is now failing to compile with the following message:

Caused by: java.lang.IllegalStateException: For MAC signing you do not need to specify the verifier key separately, and if you do it must match the signing key

KellyM
  • 2,472
  • 6
  • 46
  • 90
  • Hi Kelly, I am attempting the same thing, what class do i need to split out into the authentication and resource server? Thanks – seanie_oc Apr 01 '19 at 16:03
  • @seanie_oc, it would depend on your application and how it is currently structured. I had two separate Spring boot apps. One had a `@Configuration` class annotated with `@EnableResourceServer` and extending `ResourceServerConfigurerAdapter`. The other had a`@Configuration` class annotated with `@EnableAuthorizationServer` and extending `AuthorizationServerConfigurerAdapter`. The tutorial noted in the opening post provides some very useful information for getting started. Hope this helps. – KellyM Apr 02 '19 at 11:46
  • thanks for the tips I followed the same tutorial that you mentioned at the beginning but am a little confused on what goes where – seanie_oc Apr 02 '19 at 12:59

2 Answers2

8

I tried spring oauth and I came across the same error :

Caused by: java.lang.IllegalStateException: For MAC signing you do not need to specify the verifier key separately, and if you do it must match the signing key

My mistake was that my public certificate was :

-----BEGIN PUBLIC KEY-----
tadadada
-----END PUBLIC KEY-----
-----BEGIN CERTIFICATE-----
tadadada
-----END CERTIFICATE-----

And this is not allowed. REMOVE the certificate, just let the public key in this file :

-----BEGIN PUBLIC KEY-----
tadadada
-----END PUBLIC KEY-----

And the startup error will disapear.

For your second question, that's what I understand :

  • The authentication server give you an encrypted token (encrypted with the private key), that contains ALL the permissions of your user.

  • The resource server decrypts the token with the public key, and assumes that the permissions contained in the token are TRUE.

Hopes this help.

Oreste Viron
  • 3,592
  • 3
  • 22
  • 34
  • 1
    Thanks much. I recall that I actually made a similar mistake, except I removed the --BEGIN PUBLIC KEY -- and -- END PUBLIC KEY -- lines. For anyone who stumbles across this issue, as noted by @Oreste Viron, you need to remove the certificate portion, but actually keep the -- --- lines that contain the public key. – KellyM Jan 11 '18 at 23:43
  • Thanks. I omitted the -- label for both private and public key. I thought they were not needed – mumbasa Mar 17 '20 at 09:26
0

I ran into this issue, when my public key was formatted as below:

"-----BEGIN RSA PUBLIC KEY-----\n${encoder.encodeToString(keyPair.public.encoded)}\n-----END RSA PUBLIC KEY-----\n"

When I changed this to:

"-----BEGIN PUBLIC KEY-----\n${encoder.encodeToString(keyPair.public.encoded)}\n-----END PUBLIC KEY-----\n"

The key was accepted.

Bas Kuis
  • 748
  • 1
  • 7
  • 20
  • 2
    This is not helpful at all. How did you change your code? I have no public keys in my JWT code but I get this error – parsecer Jun 23 '20 at 18:11
  • Late reply - but hopefully it helps someone else. In our case - we exposed our own public key endpoint. The example above is the public key - which @ResourceServer annotated spring projects can read (from a rest endpoint in my case). Hope that helps. – Bas Kuis Apr 27 '22 at 18:50