Skip to content

Single page application (SPA) not redirected to OAuth2 provider via Spring Gateway #10843

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
straurob opened this issue Feb 16, 2022 · 5 comments
Assignees
Labels
for: stackoverflow A question that's better suited to stackoverflow.com

Comments

@straurob
Copy link

straurob commented Feb 16, 2022

Preface

I'm trying to setup a proof-of-concept like in the following diagram:

  • SPA is a VueJS application.
  • Gateway and API service are Spring Boot applications (version 2.6.3).
  • OAuth2 provider is Keycloak.

component overview

Observations

Scenario 1 (browser without SPA)

When I use the browser without the SPA, then the following workflow works as intended:

  1. Browser hits http://localhost:8555/api/messages.
  2. Gateway redirects to Keycloak login form.
  3. After successful authentication, the browser can access http://localhost:8555/api/messages and a SESSION cookie is created.
  4. The response from /api/messages is shown in the browser.

Scenario 2 (browser with SPA)

This is the scenario I'd like to actually realize. The observed workflow here is:

  1. User calls SPA on http://localhost:8093.
  2. SPA fires a GET request to http://localhost:8555/api/messages.
  3. The user is not redirected to the authentication provider.

Instead, there a several messages in the browser console indicating CORS issues.

Console

Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8080/auth/realms/demo/protocol/openid-connect/auth?response_type=code&client_id=my-client&state=vpgnHaxh-5nb_LxksYL1a-PRU0tzuHR5itVvw1ovD-E%3D&redirect_uri=http://localhost:8555/login/oauth2/code/keycloak. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 200.

Network Traffic

CORS messages in console

As far as I can see, the following is happening:

  1. GET to http://localhost:8555/api/messages is answered with a 302 and location /oauth2/authorization/keycloak.
  2. Another GET to http://localhost:8555/oauth2/authorization/keycloak is answered with a 302 and Location: http://localhost:8080/auth/realms/demo/protocol/openid-connect/auth?response_type=code&client_id=my-client&state=vpgnHaxh-5nb_LxksYL1a-PRU0tzuHR5itVvw1ovD-E%3D&redirect_uri=http://localhost:8555/login/oauth2/code/keycloak
  3. GET to http://localhost:8080/auth/realms/demo/protocol/openid-connect/auth?response_type=code&client_id=my-client&state=vpgnHaxh-5nb_LxksYL1a-PRU0tzuHR5itVvw1ovD-E%3D&redirect_uri=http%3A%2F%2Flocalhost%3A8555%2Flogin%2Foauth2%2Fcode%2Fkeycloak is answered with 200.

But all of them have CORS issues.

Spring Boot Implementation

Gateway

application.yml

server:
  port: 8555

spring:
  security:
    oauth2:
      client:
        provider:
          keycloak:
            issuer-uri: http://localhost:8080/auth/realms/demo
            user-name-attribute: preferred_username
        registration:
          keycloak:
            provider: keycloak
            client-id: my-client
            client-secret: my-secret
            authorization-grant-type: authorization_code
            redirect-uri: "{baseUrl}/login/oauth2/code/keycloak"
  cloud:
    gateway:
      default-filters:
        - TokenRelay
        - RemoveRequestHeader=Cookie
      routes:
        - id: api-service
          uri: http://localhost:8092
          predicates:
            - Path=/api/**

SecurityConfiguration

@Configuration
@EnableWebFluxSecurity
public class SecurityConfiguration {

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(Arrays.asList("*"));
        configuration.setAllowedHeaders(Arrays.asList("*"));
        configuration.setAllowedMethods(Arrays.asList("*"));
        configuration.setExposedHeaders(Arrays.asList("*"));

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);

        return source;
    }

    @Bean
    public SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity httpSecurity) {
        httpSecurity
                .csrf().disable()
                .oauth2Login()
                .and()
                .authorizeExchange().anyExchange().authenticated();
        return httpSecurity.build();
    }
}

Keycloak Client

Keycloak client configuration

@straurob straurob added status: waiting-for-triage An issue we've not yet triaged type: enhancement A general enhancement labels Feb 16, 2022
@sjohnr
Copy link
Member

sjohnr commented Feb 16, 2022

Hi @straurob. Thanks for your interest in the project!

I've added a comment for possible resources to check out to work through the CORS issue. For reference, it's not required to cross-post from Stack Overflow as the team monitors the spring-security tag regularly, and it's best to ask for help working through issues like this one over there. I'm going to close this issue as a question for stack overflow.

@sjohnr sjohnr closed this as completed Feb 16, 2022
@sjohnr sjohnr added for: stackoverflow A question that's better suited to stackoverflow.com and removed status: waiting-for-triage An issue we've not yet triaged type: enhancement A general enhancement labels Feb 16, 2022
@sjohnr sjohnr self-assigned this Feb 16, 2022
@razvanmazilu99
Copy link

Hello, @straurob, I have the same problem, and from what I see, there isn't an answer to our problem here on GitHub, even though the issue was closed. I also tried to access the link from @sjohnr's comment, but it is not available anymore. Did you find a solution? Can you help me?

@straurob
Copy link
Author

@razvanmazilu99 I managed to find a solution. It is not related to Spring Security, but I will post the approch here anyway for documentation purposes.

The problem, as far as I can tell, was that the frontend application issued requests using the axios library. When such a request hits a secured resource method, Spring Security will answer with a redirect to Keycloak. Redirects won't work in this case as it's the browser itself which needs to actually call http://localhost:8500/oauth2/authorization/keycloak.

I solved this by catching any 401 response in my frontend application and then redirecting the user agent. Something like this:

if (error.response.status === 401) {
    redirect('http://localhost:8500/oauth2/authorization/keycloak');
}

@razvanmazilu99
Copy link

@straurob thank you very much!!!

@uniquejava
Copy link

@straurob Thank you for your fine compiled question. The way you structuring spring security apps and the way you debug things is quite inspiring to me. I'm researching how to implement the same architecture with new Spring Authorization Server, Spring Cloud Gateway and Vue3. 😃

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
for: stackoverflow A question that's better suited to stackoverflow.com
Projects
None yet
Development

No branches or pull requests

4 participants