Reputation: 29
I'm trying to authenticate API-calls with my spring-boot backend by giving it an access token that I've gotten from Google.
From what I understand of the documentation it should be enough to just declare
security.oauth2.resource.jwk.key-set-uri=https://www.googleapis.com/oauth2/v3/certs
in the application.properties
file along with enabling resource server and web security.
The token is being sent in in the header on the form
'Authorization': 'Bearer ya29.ImCQBz5-600zVNsB[...]ka-x5kC[...]hvw-BGf3m5Bck-HF[...]44'
When I try to authenticate I get a 401 Unauthorized error with the following console error:
OAuth2AuthenticationProcessingFilter: Authentication request failed: error="invalid_token", error_description="An I/O error occurred while reading the JWT: Invalid UTF-8 start byte 0xad at [Source: (byte[])"??"; line: 1, column: 3]
I'm hoping to use most of what I can of the spring security libraries, but I've tried to write my own simple beans for token management.
@Configuration
@EnableResourceServer
@EnableWebSecurity
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().hasRole("USER");
}
@Bean
public TokenStore tokenStore() {
return new jwkTokenStore("https://www.googleapis.com/oauth2/v3/certs");
}
@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
return defaultTokenServices;
}
@Override
public void configure(ResourceServerSecurityConfigurer config) {
config.tokenServices(tokenServices());
}
}
I expect to authenticate the token and be able to display information.
Do I need to write my own functions to handle this?
Upvotes: 2
Views: 5771
Reputation: 1693
I had the same problem. It turns out that you need to import specific jwt dependencies, the default oauth2 dependencies will not work with key-set-uri.
My dependecies that I use:
'org.springframework.cloud:spring-cloud-starter-security',
'org.springframework.security:spring-security-oauth2-jose',
'org.springframework.security:spring-security-oauth2-resource-server',
The second one is the most important.
Now, you will have the JwtDecoder
and NimbusJwtDecoderJwkSupport
on your classpath you can configure SpringBoot.
This is my setup :
@NoArgsConstructor
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
class WebAppConfig extends WebSecurityConfigurerAdapter {
@Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
private String issuer;
@Value("${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}")
private String jwkSetUri;
@Override
public void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
...
.and()
.oauth2ResourceServer()
.jwt()
.decoder(decoder());
}
private JwtDecoder decoder() {
List<OAuth2TokenValidator<Jwt>> validators = new ArrayList<>();
validators.add(new JwtTimestampValidator());
validators.add(new JwtIssuerValidator(issuer));
validators.add(new TokenSupplierValidator(List.of(StringUtils.split(androidClientId,","))));
NimbusJwtDecoderJwkSupport decoder = new NimbusJwtDecoderJwkSupport(jwkSetUri)
decoder.setJwtValidator(new DelegatingOAuth2TokenValidator<>(validators));
return decoder;
}
}
If you need only default JWT token validation, you can go with the default validation (no need for setting up the custom validator).
Hope this helps!
Upvotes: 1
Reputation: 620
Maybe you must implement the WebSecurityConfigurerAdapter
@Configuration
@RequiredArgsConstructor
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class RestSecurityConfig extends WebSecurityConfigurerAdapter {
private final AADAppRoleStatelessAuthenticationFilter appRoleAuthFilter;
private final RestAuthenticationEntryPoint unauthorizedHandler;
private final RestAccessDeniedHandler accessDeniedHandler;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
http.authorizeRequests()
.antMatchers("/actuator/refresh").hasRole("Admin")
.antMatchers("/actuator/health").permitAll()
.anyRequest().fullyAuthenticated();
http.addFilterBefore(appRoleAuthFilter, UsernamePasswordAuthenticationFilter.class);
http.exceptionHandling()
.accessDeniedHandler(accessDeniedHandler)
.authenticationEntryPoint(unauthorizedHandler);
}
}
Upvotes: 1