Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring Cloud Gateway redirects to Keycloak login page although Bearer token is set

I am using a setup with Keycloak as Identity Provider, Spring Cloud Gateway as API Gateway and multiple Microservices. I can receive a JWT via my Gateway (redirecting to Keycloak) via http://localhost:8050/auth/realms/dev/protocol/openid-connect/token.

I can use the JWT to access a resource directly located at the Keycloak server (e.g. http://localhost:8080/auth/admin/realms/dev/users). But when I want to use the Gateway to relay me to the same resource (http://localhost:8050/auth/admin/realms/dev/users) I get the Keycloak Login form as response.

My conclusion is that there must me a misconfiguration in my Spring Cloud Gateway application.

This is the Security Configuration in the Gateway:

@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class SecurityConfiguration {

    @Bean
    public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http, ReactiveClientRegistrationRepository clientRegistrationRepository) {

        // Authenticate through configured OpenID Provider
        http.oauth2Login();

        // Also logout at the OpenID Connect provider
        http.logout(logout -> logout.logoutSuccessHandler(
                new OidcClientInitiatedServerLogoutSuccessHandler(clientRegistrationRepository)));

        //Exclude /auth from authentication
        http.authorizeExchange().pathMatchers("/auth/realms/ahearo/protocol/openid-connect/token").permitAll();

        // Require authentication for all requests
        http.authorizeExchange().anyExchange().authenticated();

        // Allow showing /home within a frame
        http.headers().frameOptions().mode(Mode.SAMEORIGIN);

        // Disable CSRF in the gateway to prevent conflicts with proxied service CSRF
        http.csrf().disable();
        return http.build();
    }
}

This is my application.yaml in the Gateway:

spring:
  application:
    name: gw-service
  cloud:
    gateway:
      default-filters:
        - TokenRelay
      discovery:
        locator:
          lower-case-service-id: true
          enabled: true
      routes:
        - id: auth
          uri: http://localhost:8080
          predicates:
            - Path=/auth/**

  security:
    oauth2:
      client:
        registration:
          keycloak:
            client-id: 'api-gw'
            client-secret: 'not-relevant-but-correct'
            authorizationGrantType: authorization_code
            redirect-uri: '{baseUrl}/login/oauth2/code/{registrationId}'
            scope: openid,profile,email,resource.read
        provider:
          keycloak:
            issuerUri: http://localhost:8080/auth/realms/dev
            user-name-attribute: preferred_username

server:
  port: 8050
eureka:
  client:
    service-url:
      default-zone: http://localhost:8761/eureka
    register-with-eureka: true
    fetch-registry: true

How can I make the Gateway able to know that the user is authenticated (using the JWT) and not redirect me to the login page?

like image 414
Felix Avatar asked Oct 16 '22 02:10

Felix


1 Answers

If you want to make requests to Spring Gateway with access token you need to make it a resource server. Add the following:

pom.xml

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
    </dependency>

application.yml

  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://.../auth/realms/...

SecurityConfiguration.java

@Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http,
                                                        ReactiveClientRegistrationRepository clientRegistrationRepository) {
    // Authenticate through configured OpenID Provider
    http.oauth2Login();
    // Also logout at the OpenID Connect provider
    http.logout(logout -> logout.logoutSuccessHandler(
            new OidcClientInitiatedServerLogoutSuccessHandler(clientRegistrationRepository)));
    // Require authentication for all requests
    http.authorizeExchange().anyExchange().authenticated();

    http.oauth2ResourceServer().jwt();

    // Allow showing /home within a frame
    http.headers().frameOptions().mode(Mode.SAMEORIGIN);
    // Disable CSRF in the gateway to prevent conflicts with proxied service CSRF
    http.csrf().disable();
    return http.build();
}
like image 92
Dmitri Ciornii Avatar answered Oct 23 '22 18:10

Dmitri Ciornii