title: Spring Cloud Gateway - opaque tokens
body: <p>I'm currently looking at implementing an API Gateway using Spring Cloud Gateway. There will be some React clients to the APIs and also some devices. The devices will be granted access using OAuth device_code grant.
  The API gateway will pass JWT to back end resource API servers, which will validate the request. I have followed the following example which seems to get me part of the way there.</p>
  <p><a href="https://spring.io/blog/2019/08/16/securing-services-with-spring-cloud-gateway" rel="nofollow noreferrer">https://spring.io/blog/2019/08/16/securing-services-with-spring-cloud-gateway</a>
  I've also been able to swap out the IdP in that example for WSO2.</p>
  <p>I obtained a bearer token from the IdP using curl in order to test using an opaque token.</p>
  <p><code>curl -u OzVconnyapPW2yWzxrSebCKmY9Qa:5cBcZmnnaW5gGOW3qt9sumw4Ubka -k -d &quot;grant_type=password&amp;username=admin&amp;password=admin&quot; -H &quot;Content-Type:application/x-www-form-urlencoded&quot; https://localhost:9443/oauth2/token</code></p>
  <p>which returned me an access token.</p>
  <p><code>curl -k http://springboot.example.com:8080/ -H &quot;Authorization: Bearer 123b546d-f35b-38b8-a2c8-4e0d4487329d&quot;</code></p>
  <p>But this tries to redirect me to login.</p>
  <p>How can I get Spring Cloud API Gateway to process requests where an <code>Authorization: Bearer xxxx</code> header has been added?
  Am I using the correct 'opaque token' (i.e. is an Authorization token the correct way to do this)?
  Can the oauth2 client be bypassed if an Authorization header is set and could a resource server be triggered instead?</p>
  <p>This diagram represents my current thinking, which could be wrong (please say if so!)
  <a href="https://i.stack.imgur.com/G1Fa9.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/G1Fa9.png" alt="API Design" /></a></p>
  <p>API Gateway app:</p>
  <pre class="lang-java prettyprint-override"><code>package com.scg.gateway;
  
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.boot.SpringApplication;
  import org.springframework.boot.autoconfigure.SpringBootApplication;
  import org.springframework.cloud.gateway.route.RouteLocator;
  import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
  import org.springframework.cloud.security.oauth2.gateway.TokenRelayGatewayFilterFactory;
  import org.springframework.context.annotation.Bean;
  import org.springframework.security.core.annotation.AuthenticationPrincipal;
  import org.springframework.security.oauth2.client.OAuth2AuthorizedClient;
  import org.springframework.security.oauth2.client.annotation.RegisteredOAuth2AuthorizedClient;
  import org.springframework.security.oauth2.core.user.OAuth2User;
  import org.springframework.stereotype.Controller;
  import org.springframework.ui.Model;
  import org.springframework.web.bind.annotation.GetMapping;
  
  @Controller
  @SpringBootApplication
  public class GatewayApplication {
  
      @Autowired
      private TokenRelayGatewayFilterFactory filterFactory;
  
      @Bean
      public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
          return builder.routes()
                  .route(&quot;resource&quot;, r -&gt; r.path(&quot;/resource&quot;)
                      .filters(f -&gt; f.filters(filterFactory.apply())
  
                                      .removeRequestHeader(&quot;Cookie&quot;)) // Prevents cookie being sent downstream
                      .uri(&quot;http://springboot.example.com:9000&quot;)) // Taking advantage of docker naming
                  .build();
      }
  
      @GetMapping(&quot;/&quot;)
      public String index(Model model,
                          @RegisteredOAuth2AuthorizedClient OAuth2AuthorizedClient authorizedClient,
                          @AuthenticationPrincipal OAuth2User oauth2User) {
          model.addAttribute(&quot;userName&quot;, oauth2User.getName());
          model.addAttribute(&quot;clientName&quot;, authorizedClient.getClientRegistration().getClientName());
          model.addAttribute(&quot;userAttributes&quot;, oauth2User.getAttributes());
          return &quot;index&quot;;
      }
  
      public static void main(String[] args) {
          SpringApplication.run(GatewayApplication.class, args);
      }
  }
  </code></pre>
  <p>API Gateway Yaml</p>
  <pre class="lang-yaml prettyprint-override"><code>server:
    port: 8080
  
  logging:
    level:
      root: INFO
      org.springframework.web: INFO
      org.springframework.web.HttpLogging: DEBUG
      org.springframework.security: DEBUG
      org.springframework.security.oauth2: DEBUG
      org.springframework.cloud.gateway: DEBUG
  
  spring:
    autoconfigure:
      # TODO: remove when fixed https://github.com/spring-projects/spring-security/issues/6314
      exclude: org.springframework.boot.actuate.autoconfigure.security.reactive.ReactiveManagementWebSecurityAutoConfiguration
    thymeleaf:
      cache: false
    security:
      oauth2:
        client:
          registration:
            gateway:
              provider: wso2
              client-id: OzVconnyapPW2yWzxrSebCKmY9Qa
              client-secret: 5cBcZmnnaW5gGOW3qt9sumw4Ubka
              authorization-grant-type: authorization_code
              redirect-uri-template: &quot;{baseUrl}/login/oauth2/code/wso2&quot;
              scope: openid,profile,email,resource.read
          provider:
            wso2:
              authorization-uri: http://idp.example.com:9763/oauth2/authorize
              token-uri: http://idp.example.com:9763/oauth2/token
              user-info-uri: http://idp.example.com:9763/oauth2/userinfo
              user-name-attribute: sub
              jwk-set-uri: http://idp.example.com:9763/oauth2/jwks
  </code></pre>