Hagen
Hagen

Reputation: 29

Verifying access token from Google using spring security

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

Answers (2)

Sniady
Sniady

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

baliman
baliman

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

Related Questions