VostanAzatyan
VostanAzatyan

Reputation: 647

Spring Cloud Gateway + Spring security resource server

I really would not put it here but I am really confused, I want to achieve the following.

I am running

Now I want to integrate security to my Gateway and to all the downstream microservices. Eventually, I decided to go with Firebase as an Identity Provider (IDP). My Angular application will get JWT token from Firebase and send it in every request to Cloud Gateway. So, the Gateway will start to act as a resource server ONLY and that is it.

Here how I tried to give it a go. Set up and Spring Cloud Gateway to act like Resource Server at the same time. Quite well explained here Spring Security Docs.

Here what my configuration looks like

@EnableWebFluxSecurity
public class ResourceServerSecurityConfiguration {

  @Bean
  public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
    // @formatter:off
    http
        .authorizeExchange()
        .anyExchange().authenticated()
        .and()
        .oauth2ResourceServer()
        .jwt();
    return http.build();
    // @formatter:on
  }
}

And application.yml

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          jwk-set-uri: https://www.googleapis.com/service_accounts/v1/jwk/[email protected]
          issuer-uri: https://securetoken.google.com/{$app.name}

As you see in this YAML I provided the jwk-set-uri and issuer to validate incoming tokens.

At this point, all work quite as accepted. All the requests have to have valid JWT in the Authentication header.

Next,

I want my gateway to use WebClient and call several services to aggregate data for the frontend.

Here how I am trying to configure my client.

  @Bean
  @LoadBalanced
  public WebClient.Builder loadBalancedWebClientBuilder() {
    return WebClient.builder()
        .filter(new ServletBearerExchangeFilterFunction());
  }

As you see It uses ServletBearerExchangeFilterFunction this is where my real problem comes in.

I already checked that when Spring configuring oauth2ResourceServer it uses NoOpServerSecurityContextRepository. From what I understand so far that this is exactly a repository that used to register context per request. Also, I understand that it makes sense to use NoOp as we want to be stateless. However what I do not understand how to make ServletBearerExchangeFilterFunction to work properly and pass downstream my tokens.

I spend now quite a lot of time trying to figure out the correct way of doing this.

Found this: Spring Boot 2 OIDC (OAuth2) client / resource server not propagating the access token in the WebClient

Github: https://github.com/spring-projects/spring-security/issues/7771

And even according to this what I try to do should be legit and possible. Not sure where I am mistaken.

Upvotes: 2

Views: 13684

Answers (2)

VostanAzatyan
VostanAzatyan

Reputation: 647

I figured this, the thing is that ReactiveSecurityContext is available only when you are within the reactive flow and ServletBearerExchangeFilterFunction meant to be for Servlet calls.

Eventually, I just wrote my own filter function which listens to ReactiveSecurity context and sets the Authorization header. Here it is.

public class BearerExchangeFilterFunction implements ExchangeFilterFunction {

  @Override
  @NonNull
  public Mono<ClientResponse> filter(@NonNull ClientRequest request, ExchangeFunction next) {
    return ReactiveSecurityContextHolder.getContext()
        .map(c -> (c.getAuthentication().getCredentials()))
        .cast(AbstractOAuth2Token.class)
        .checkpoint()
        .map(token -> bearer(request, token))
        .defaultIfEmpty(request)
        .flatMap(next::exchange);
  }

  private ClientRequest bearer(ClientRequest request, AbstractOAuth2Token token) {
    return ClientRequest.from(request)
        .headers(headers -> headers.setBearerAuth(token.getTokenValue()))
        .build();
  }


}

Upvotes: 3

user3940641
user3940641

Reputation:

Spring Cloud Gateway aims to provide a simple, yet effective way to route to APIs and provide cross cutting concerns to them such as: security, monitoring/metrics, and resiliency.

If you set up SCG(Spring Cloud Gateway) as oauth2 resource server you must do more custom,Maybe like this. I think you should not do that.

You can use gateway use as route,and send access token header to SCG,and SCG will take access token to oauth2 resource server,and check permission on oauth2 resource server side.

Spring Cloud Gateway Token Relay GatewayFilter Factory say:

The {githubmaster}/src/main/java/org/springframework/cloud/gateway/security/TokenRelayGatewayFilterFactory.java[filter] extracts an access token from the currently authenticated user, and puts it in a request header for the downstream requests.

So that can keep oauth2 clearness in you app.

Upvotes: 1

Related Questions