Search code examples
javaspring-securityspring-security-oauth2spring-oauth2

Spring Security Returns with 403 when there is no Bearer token


I could not find a proper solution to my problem yet. I'm using Spring Security Oauth2 resource server to authenticate my requests. And that works fine. But when tested with different scenario it is found that spring security returns with 403 instead of 401 if there is no Authorization header present or if there is Authorization header present but the value doesn't begin with Bearer .

Spring Boot Starter - 2.6.7
Spring Boot Starter Security - 2.6.7
Spring Security Config & Web - 5.6.3
Spring Security Core - 5.3.19
Spring Boot Starter OAuth2 Resource Server - 2.6.7
Spring OAuth2 Resource Server - 5.6.3

I was referring to this answer and added below code for BearerTokenAuthenticationEntryPoint. The difference is I'm using introspection url instead jwt. But it doesn't help and that part doesn't get executed. If the Bearer token is present, then only it gets executed.

What am I missing here?

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class CustomResourceServerSecurityConfiguration {

    @Value("${spring.security.oauth2.resourceserver.opaque-token.introspection-uri}")
    String introspectionUri;

    @Value("${spring.security.oauth2.resourceserver.opaque-token.client-id}")
    String clientId;

    @Value("${spring.security.oauth2.resourceserver.opaque-token.client-secret}")
    String clientSecret;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {

        http.authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())
                .oauth2ResourceServer(oauth2 -> oauth2
                        .opaqueToken(opaque -> opaque.introspectionUri(this.introspectionUri)
                                .introspectionClientCredentials(this.clientId, this.clientSecret))
                        .authenticationEntryPoint((request, response, exception) -> {
                            System.out.println("Authentication failed");
                            BearerTokenAuthenticationEntryPoint delegate = new BearerTokenAuthenticationEntryPoint();
                            delegate.commence(request, response, exception);
                        }))

                .exceptionHandling(
                        (exceptions) -> exceptions.authenticationEntryPoint((request, response, exception) -> {
                            System.out.println("Authentication is required");
                            BearerTokenAuthenticationEntryPoint delegate = new BearerTokenAuthenticationEntryPoint();
                            delegate.commence(request, response, exception);
                        }));
        return http.build();
    }
}

Solution

  • If your scenario is that you get 403 for POST and 401 for GET if the Bearer token is missing it's related to csrf.

    44.2.14 I get a 403 Forbidden when performing a POST

    If an HTTP 403 Forbidden is returned for HTTP POST, but works for HTTP GET then the issue is most likely related to CSRF. Either provide the CSRF Token or disable CSRF protection (not recommended).

    Here's source

    and if you use JWT token then if doesn't have any ther additional requirements then you can disable this.

      @Bean
        public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
              http.authorizeHttpRequests(authorize -> authorize
                            .anyRequest().authenticated())
                            .csrf().disable()           
                     return http.build();
        } 
    

    4. Stateless Spring API

    If our stateless API uses token-based authentication, such as JWT, we don't need CSRF protection, and we must disable it as we saw earlier

    Source