I'm using Spring Boot 2.1.5 and creating two separate modules for Authorization Server and Resource Server. I recently shifted to use JWT instead of normal access tokens using the examples shown here and here.
Now the error which got me here is this
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
I had a look at this answer but that's not the case with me. I have generated proper files using keytool and saved them to my resources folder.
- JKS file in the resources folder of Authorization Server module
- public_key.txt in the resources folder of Resource Server module
Here is my Authorization Server Configuration File
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(AuthorizationServerConfiguration.class);
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private DataSource dataSource;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public void configure(final ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(this.dataSource);
}
@Override
public void configure(final AuthorizationServerEndpointsConfigurer endpoints) {
TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
tokenEnhancerChain.setTokenEnhancers(
Arrays.asList(tokenEnhancer(), jwtAccessTokenConverter()));
endpoints.authenticationManager(authenticationManager)
.userDetailsService(userDetailsService)
.tokenEnhancer(tokenEnhancerChain)
.accessTokenConverter(jwtAccessTokenConverter())
.tokenStore(tokenStore());
}
@Override
public void configure(final AuthorizationServerSecurityConfigurer oauthServer) {
oauthServer.passwordEncoder(passwordEncoder).tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()");
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
KeyStoreKeyFactory keyStoreKeyFactory =
new KeyStoreKeyFactory(new ClassPathResource("accesstoken_keystore.jks"), "my-keystore-password".toCharArray());
jwtAccessTokenConverter.setKeyPair(keyStoreKeyFactory.getKeyPair("my-alias"));
return jwtAccessTokenConverter;
}
@Bean
public TokenEnhancer tokenEnhancer() {
return new CustomTokenEnhancer();
}
@Bean
public DefaultTokenServices tokenServices(final TokenStore tokenStore,
final ClientDetailsService clientDetailsService) {
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setSupportRefreshToken(true);
tokenServices.setTokenStore(tokenStore);
tokenServices.setClientDetailsService(clientDetailsService);
tokenServices.setAuthenticationManager(authenticationManager);
return tokenServices;
}
}
And Here is my Resource Server Configuration class
@Configuration
@EnableResourceServer
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
private static final Logger logger = LoggerFactory.getLogger(ResourceServerConfig.class);
@Autowired
private Environment env;
@Autowired
private CustomAccessTokenConverter accessTokenConverter;
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/**").access("#oauth2.hasScope('read')")
.anyRequest().authenticated()
.and().exceptionHandling().accessDeniedHandler(new OAuth2AccessDeniedHandler());
}
@Override
public void configure(ResourceServerSecurityConfigurer config) {
config.tokenServices(tokenServices());
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
Resource publicKeyResource = new ClassPathResource("public_key.txt");
String publicKey = null;
try {
publicKey = new String(FileCopyUtils.copyToByteArray(publicKeyResource.getInputStream()));
} catch (final IOException e) {
throw new RuntimeException(e);
}
converter.setVerifierKey(publicKey); //this is where I'm getting the error
return converter;
}
@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
return defaultTokenServices;
}
And here is the content of public_key.txt file as it is
-----BEGIN PUBLIC KEY-----
blah blah blah
blah blah blah
-----END PUBLIC KEY-----
I'm expecting that the Authorization Server will sign the JWT using the Private Key extracted from the KeyStore file and Resource Server will just verify it.
Not sure what's wrong. Everything was working fine before I decided to use a RSA key asymmetric pair.