Spring Security OAuth2 Userinfo returns HTTP 403 Forbidden

I have a simple Spring Boot resource server that is configured with both @EnableResourceServer and @EnableOAuth2Sso. I specified the following in my application.yml file to configure the user info endpoint:

security:
  oauth2:
    resource:
      user-info-uri: https://dev-542348.okta.com/oauth2/default/v1/userinfo

When I try to perform a GET /v1/secured against my application (which is also secured with @PreAuthorize('isAuthenticated()'), it tries to hit the /v1/userinfo endpoint and responds with an HTTP 403 Forbidden:

2020-08-20 17:26:39.696 DEBUG 22960 --- [nio-8080-exec-2] o.s.s.oauth2.client.OAuth2RestTemplate   : HTTP GET https://dev-542348.okta.com/oauth2/default/v1/userinfo
2020-08-20 17:26:39.708 DEBUG 22960 --- [nio-8080-exec-2] o.s.s.oauth2.client.OAuth2RestTemplate   : Accept=[application/json, application/*+json]
2020-08-20 17:26:40.783 DEBUG 22960 --- [nio-8080-exec-2] o.s.s.oauth2.client.OAuth2RestTemplate   : Response 403 FORBIDDEN
2020-08-20 17:26:40.789  WARN 22960 --- [nio-8080-exec-2] o.s.b.a.s.o.r.UserInfoTokenServices      : Could not fetch user details: class org.springframework.web.client.HttpClientErrorException$Forbidden, 403 Forbidden: [no body]
2020-08-20 17:26:40.789 DEBUG 22960 --- [nio-8080-exec-2] p.a.OAuth2AuthenticationProcessingFilter : Authentication request failed: error="invalid_token", error_description="eyJraWQiOiJFbThlVjZwNDF6SDIya2MzR3V1NjFKWTdBelJmdzNkWlNGRVZ4UHZBUTIwIiwiYWxnIjoiUlMyNTYifQ.eyJ2ZXIiOjEsImp0aSI6IkFULmdpd0NRUks1eWNTLU1qTFRVcjJTTXEzNHE5aVZNNnlydENJY0hGSFBZSUkiLCJpc3MiOiJodHRwczovL2Rldi01NDIzNDgub2t0YS5jb20vb2F1dGgyL2RlZmF1bHQiLCJhdWQiOiJhcGk6Ly9kZWZhdWx0IiwiaWF0IjoxNTk3OTYxNTM5LCJleHAiOjE1OTc5NjUxMzksImNpZCI6IjBvYXF3dTBmNUc4MVF3REEzNHg2IiwidWlkIjoiMDB1cXdzMzI5ZmRPRlNtTkk0eDYiLCJzY3AiOlsiZGVmYXVsdC1zY29wZSJdLCJzdWIiOiJqZGlldHpAZm5uaS5jb20ifQ.deGYyQu-L6fKXiOtLS1NZi19rz4AHukwGeHpcK20gDPy6FMTYvswBOd6nCChWmLDNI4cAcaFm8Fb0IAChzEikNwzNkaGGX9BdMxozusdAY9LEHx3ia4MzMBrjdTdthzhUaaxANH5R2ScODHzuSrR3r6c37S98lSfIkCwd_Wlxp_Z3dcBJm8rJWXE4XmN-rz7oxm4-dxBjtxd9IGG3EE2c86_5CkAhb4C0UQRW_BQ6-akXKp31n0-3wpLZVTt8UNpgN0d-GMrpnv-9xQ_HP3ic8PkpjgCuhoGCZ04E8sDiM8CowKe_eg9wHO0hHfIPX5v_87w_KM6eG4Z1IwL5U3AlQ"
2020-08-20 17:26:40.815 DEBUG 22960 --- [nio-8080-exec-2] o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@582831a6
2020-08-20 17:26:40.821 DEBUG 22960 --- [nio-8080-exec-2] s.s.o.p.e.DefaultOAuth2ExceptionRenderer : Written [error="invalid_token", error_description="eyJraWQiOiJFbThlVjZwNDF6SDIya2MzR3V1NjFKWTdBelJmdzNkWlNGRVZ4UHZBUTIwIiwiYWxnIjoiUlMyNTYifQ.eyJ2ZXIiOjEsImp0aSI6IkFULmdpd0NRUks1eWNTLU1qTFRVcjJTTXEzNHE5aVZNNnlydENJY0hGSFBZSUkiLCJpc3MiOiJodHRwczovL2Rldi01NDIzNDgub2t0YS5jb20vb2F1dGgyL2RlZmF1bHQiLCJhdWQiOiJhcGk6Ly9kZWZhdWx0IiwiaWF0IjoxNTk3OTYxNTM5LCJleHAiOjE1OTc5NjUxMzksImNpZCI6IjBvYXF3dTBmNUc4MVF3REEzNHg2IiwidWlkIjoiMDB1cXdzMzI5ZmRPRlNtTkk0eDYiLCJzY3AiOlsiZGVmYXVsdC1zY29wZSJdLCJzdWIiOiJqZGlldHpAZm5uaS5jb20ifQ.deGYyQu-L6fKXiOtLS1NZi19rz4AHukwGeHpcK20gDPy6FMTYvswBOd6nCChWmLDNI4cAcaFm8Fb0IAChzEikNwzNkaGGX9BdMxozusdAY9LEHx3ia4MzMBrjdTdthzhUaaxANH5R2ScODHzuSrR3r6c37S98lSfIkCwd_Wlxp_Z3dcBJm8rJWXE4XmN-rz7oxm4-dxBjtxd9IGG3EE2c86_5CkAhb4C0UQRW_BQ6-akXKp31n0-3wpLZVTt8UNpgN0d-GMrpnv-9xQ_HP3ic8PkpjgCuhoGCZ04E8sDiM8CowKe_eg9wHO0hHfIPX5v_87w_KM6eG4Z1IwL5U3AlQ"] as "application/json" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@6d9ceb94]
2020-08-20 17:26:40.821 DEBUG 22960 --- [nio-8080-exec-2] s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed

I am using Spring Boot 2.3.3.RELEASE, Spring Cloud Hoxton.SR7, and the Okta Spring Boot Starter 1.4.0.

You don’t need these with Spring Boot 2.2.x:

Now it’s called oauth2Login() and oauth2ResourceServer() and you configure it as part of the Spring Security DSL. Here’s a WebFlux example:

@Bean
public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
    return http
            .csrf()
                .csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse()) 
                .and()
            .authorizeExchange()
                .pathMatchers("/ws/**").permitAll() 
                .anyExchange().authenticated()
                .and()
            .oauth2Login()
                .and()
            .oauth2ResourceServer()
                .jwt().and().and().build();
}

You can use our Spring Boot starter if you want to simplify your configuration.

Thanks for the fast response, @mraible! I decided to convert my project to be on the Reactive stack, and updated my OAuthConfig class so that it looks like this now. This will be a resource server only, so I removed the oauth2Login() portion.

@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
@Configuration
class OAuthConfig {

    @Bean
    static SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        return http
                .authorizeExchange()
                .anyExchange().authenticated()
                .and()
                .oauth2ResourceServer()
                .jwt().and().and().build()
    }
}

I think I am good to go with this issue – I have another question but I will open a new thread for it. Thanks again, @mraible!

1 Like