Search code examples
javaspring-bootspring-securityoauth-2.0csrf

How to generate and pass CSRF token for a route in spring cloud gateway with Spring Security


I'm trying to implement Oauth2 based authentication using Okta, Spring Cloud Gateway & Spring Security. The objective that I want to achieve is I want a route of spring cloud gateway to be available for all without any authentication. I know spring cloud gateway uses WebFlux. So according to that only I'm configuring Spring Security. I have three routes in spring cloud gateway.

  • /auth
  • /doctors
  • /patients

I want any of these /auth/** routes should be available to all because here my users signup so I can not put authentication there. For the other two /doctors/** and /patients/** I want authentication and Authorisation. In the SecurityConfig in cloud gateway I'm trying to achieve the same. Currently I have made this config:

    package com.sb.projects.medica.gateway.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
    import org.springframework.security.config.web.server.ServerHttpSecurity;
    import org.springframework.security.web.server.SecurityWebFilterChain;
    
    @Configuration
    @EnableWebFluxSecurity
    public class SecurityConfig {
        @Bean
        public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity httpSecurity) {
            httpSecurity.authorizeExchange(exchanges -> {
                exchanges.pathMatchers("/doctor/**", "/patient/**").authenticated();
                exchanges.pathMatchers("/auth/**").permitAll();
            }).oauth2Client().and().oauth2ResourceServer().jwt();
            return httpSecurity.build();
        }
    }

But it does not seem to work. When I try to hit any route of /auth/** I get

An expected CSRF token cannot be found

How can I solve this error. How can I create a csrf token and send it to frontend


Solution

  • You are obviously just beginning your journey to CSRF configuration. I recommand you start with the Spring documentation. Like really. Read it carefully even before the rest of this answer.

    The REST API(s) behind the gateway can probably be configured as "stateless" (http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) in a servlet or http.securityContextRepository(NoOpServerSecurityContextRepository.getInstance()) in a reactive app) and, as a consequence, CSRF protection can be disabled on it (http.csrf().disable()). You then configure Postman with OAuth2 authorization and query the API directly (not through the gateway).

    Depending on your React frontend being configured as an OAuth2 public client or the gateway as a Backend For Frontend (OAuth2 confidential client with TokenRelay filter), the needs for CSRF protection are quite different:

    • in the first case, the gateway can be transparent to security and, as it handles neither login nor logout (both are done by the OAuth2 client lib in React app), nor anything related to user session, CSRF is not a concern. However, this is now discouraged in favor of the OAuth2 BFF pattern.
    • in the second case, the gateway has to maintain sessions for authorization-code flow to succeed and for the TokenRelay filter to work (keep OAuth2 tokens associated with each browser session). As a consequence, CSRF must be enabled and, as your app is Javascript based, with Cookie repo (http.csrf().csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse()); on a reactive application like spring-cloud-gateway). You'll then have to configure your frontend to send the CSRF token (received as cookie) as X-XSRF-TOKEN header (same requirement for Postman if you want to send requests through the gateway instead of querying the API(s) directly). I wrote a complete guide on Baeldung.