spring - CORS Error with Keycloak despite correct Web Origins and setup in Docker environment - Stack Overflow

admin2025-05-02  2

I'm experiencing a persistent CORS issue when integrating Keycloak (v26.0.7) with my application running in a Docker environment. Despite having configured Web Origins and Redirect URIs correctly in Keycloak, I'm still facing CORS errors in the browser.

Environment Setup

  • Keycloak Version: 26.0.7 (running in Docker)
  • Frontend: React app at http://localhost:3000
  • Backend: Spring Gateway at http://localhost:8080
  • Docker Compose Keycloak Configuration:
saip-keycloak:
  image: quay.io/keycloak/keycloak:26.0.7
  container_name: saip-keycloak
  ports:
    - "8090:8080"
  volumes:
    - ./keycloak/dev/seed/realm-import.json:/opt/keycloak/data/import/realm-import.json
    - ./keycloak/dev/custom-theme:/opt/keycloak/themes/custom-theme
  environment:
    - KC_DB=mariadb
    - KC_DB_URL=jdbc:mariadb://saip-mariadb-auth:3306/saip_auth_db
    - KC_DB_USERNAME=auth_user1
    - KC_DB_PASSWORD=auth_user1
    - KC_BOOTSTRAP_ADMIN_USERNAME=admin
    - KC_BOOTSTRAP_ADMIN_PASSWORD=admin
    - JAVA_OPTS_APPEND=-Dkeycloak.logging.level=DEBUG
    - KC_SPI_HOSTNAME_DEFAULT_FORWARDED=true
  command: [
    "start-dev",
    "--import-realm",
    "--log-level=DEBUG",
    "--hostname=localhost"
  ]

Keycloak Settings

  1. Web Origins:
    • http://localhost:3000
    • http://localhost:8080
  2. Valid Redirect URIs:
    • http://localhost:3000/*
    • http://localhost:8080/*
  3. Valid Post Logout Redirect URIs:
    • http://localhost:3000

Problem Description

When a user accesses the frontend (http://localhost:3000) and attempts to authenticate via Keycloak, the following flow occurs:

  1. The client sends a request to the backend (http://localhost:8080/success-page).
  2. If not authenticated, the backend redirects the user to the Keycloak login page at http://localhost:8090.
  3. The browser then encounters a CORS error while trying to access Keycloak's login page or its static resources (e.g., CSS, JS).

Here's what I've verified so far:

  • CORS Headers Missing: In the browser's network tab, I can see that Access-Control-Allow-Origin is missing in the response headers from Keycloak.
  • Preflight Request Fails: The browser's OPTIONS (preflight) request to Keycloak doesn't return the required CORS headers.
  • Keycloak Logs: Debug logs don't show any errors, and authentication flow appears to be processed normally.

What I've Tried

  1. Keycloak Configuration:
    • Ensured that Web Origins and Valid Redirect URIs are correctly configured.
    • Added KC_SPI_HOSTNAME_DEFAULT_FORWARDED=true to the Keycloak Docker environment.
  2. Frontend Configuration:
    • Verified that the frontend uses the correct Keycloak client.
  3. Spring Gateway:
    • Implemented a CorsConfigurationSource to allow CORS for all paths:
@Bean
public CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.addAllowedOrigin("http://localhost:3000");
    configuration.addAllowedMethod("*");
    configuration.addAllowedHeader("*");
    configuration.setAllowCredentials(true);
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
}
  1. Keycloak HTTP Configuration:
    • Tested Keycloak with KC_RESPONSE_HEADERS to explicitly add Access-Control-Allow-Origin, but no change.
  2. Keycloak Theme:
    • Explored modifying the Keycloak login page's Freemarker template (login.ftl) to add meta tags for CORS headers but prefer not to rely on this hack.

Constraints

  • I cannot open all origins (*) due to security concerns.
  • I prefer not to use a reverse proxy to handle CORS for Keycloak.

Questions

  1. Why is Keycloak not responding with Access-Control-Allow-Origin in the CORS headers, even though Web Origins is correctly set?
  2. Is there a specific Keycloak configuration or Docker setting I'm missing to ensure proper CORS handling?
  3. Are there any known issues with CORS in Keycloak v26.0.7 or a better way to debug/prevent this issue?

Additional Context

Here's a snippet of the browser error:

Access to fetch at 'http://localhost:8090/realms/testrealm/protocol/openid-connect/auth' from origin 'http://localhost:3000' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.

Note

In my case, it is the backend server that acts as the Client in the OAuth 2.0 flow, not the frontend server.

This means that the backend server, not the frontend server, is the entity that makes authentication requests to the Keycloak authentication server.

The user only performs the authentication when requested to do so.

Flow

  1. :3000/test → Button click (request to the backend :8080/success-page)

  2. (302 redirect) → :8080/oauth2/authorization/keycloak

  3. (302 redirect) → :8090/realms/testrealm/protocol/openid-connect/auth/...

CORS Error Description

• The CORS error does not occur when redirecting from the frontend server to the Spring Boot backend server (:8080).

• However, the problem begins when redirecting from the backend server (:8080) to the Keycloak authentication server (:8090).

• During this step, the Origin header becomes null.

Keycloak detects the null origin and, as a result, does not include CORS-related headers in its response.

• When the browser receives the response from Keycloak without the required headers, it triggers a CORS error.

I'm experiencing a persistent CORS issue when integrating Keycloak (v26.0.7) with my application running in a Docker environment. Despite having configured Web Origins and Redirect URIs correctly in Keycloak, I'm still facing CORS errors in the browser.

Environment Setup

  • Keycloak Version: 26.0.7 (running in Docker)
  • Frontend: React app at http://localhost:3000
  • Backend: Spring Gateway at http://localhost:8080
  • Docker Compose Keycloak Configuration:
saip-keycloak:
  image: quay.io/keycloak/keycloak:26.0.7
  container_name: saip-keycloak
  ports:
    - "8090:8080"
  volumes:
    - ./keycloak/dev/seed/realm-import.json:/opt/keycloak/data/import/realm-import.json
    - ./keycloak/dev/custom-theme:/opt/keycloak/themes/custom-theme
  environment:
    - KC_DB=mariadb
    - KC_DB_URL=jdbc:mariadb://saip-mariadb-auth:3306/saip_auth_db
    - KC_DB_USERNAME=auth_user1
    - KC_DB_PASSWORD=auth_user1
    - KC_BOOTSTRAP_ADMIN_USERNAME=admin
    - KC_BOOTSTRAP_ADMIN_PASSWORD=admin
    - JAVA_OPTS_APPEND=-Dkeycloak.logging.level=DEBUG
    - KC_SPI_HOSTNAME_DEFAULT_FORWARDED=true
  command: [
    "start-dev",
    "--import-realm",
    "--log-level=DEBUG",
    "--hostname=localhost"
  ]

Keycloak Settings

  1. Web Origins:
    • http://localhost:3000
    • http://localhost:8080
  2. Valid Redirect URIs:
    • http://localhost:3000/*
    • http://localhost:8080/*
  3. Valid Post Logout Redirect URIs:
    • http://localhost:3000

Problem Description

When a user accesses the frontend (http://localhost:3000) and attempts to authenticate via Keycloak, the following flow occurs:

  1. The client sends a request to the backend (http://localhost:8080/success-page).
  2. If not authenticated, the backend redirects the user to the Keycloak login page at http://localhost:8090.
  3. The browser then encounters a CORS error while trying to access Keycloak's login page or its static resources (e.g., CSS, JS).

Here's what I've verified so far:

  • CORS Headers Missing: In the browser's network tab, I can see that Access-Control-Allow-Origin is missing in the response headers from Keycloak.
  • Preflight Request Fails: The browser's OPTIONS (preflight) request to Keycloak doesn't return the required CORS headers.
  • Keycloak Logs: Debug logs don't show any errors, and authentication flow appears to be processed normally.

What I've Tried

  1. Keycloak Configuration:
    • Ensured that Web Origins and Valid Redirect URIs are correctly configured.
    • Added KC_SPI_HOSTNAME_DEFAULT_FORWARDED=true to the Keycloak Docker environment.
  2. Frontend Configuration:
    • Verified that the frontend uses the correct Keycloak client.
  3. Spring Gateway:
    • Implemented a CorsConfigurationSource to allow CORS for all paths:
@Bean
public CorsConfigurationSource corsConfigurationSource() {
    CorsConfiguration configuration = new CorsConfiguration();
    configuration.addAllowedOrigin("http://localhost:3000");
    configuration.addAllowedMethod("*");
    configuration.addAllowedHeader("*");
    configuration.setAllowCredentials(true);
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", configuration);
    return source;
}
  1. Keycloak HTTP Configuration:
    • Tested Keycloak with KC_RESPONSE_HEADERS to explicitly add Access-Control-Allow-Origin, but no change.
  2. Keycloak Theme:
    • Explored modifying the Keycloak login page's Freemarker template (login.ftl) to add meta tags for CORS headers but prefer not to rely on this hack.

Constraints

  • I cannot open all origins (*) due to security concerns.
  • I prefer not to use a reverse proxy to handle CORS for Keycloak.

Questions

  1. Why is Keycloak not responding with Access-Control-Allow-Origin in the CORS headers, even though Web Origins is correctly set?
  2. Is there a specific Keycloak configuration or Docker setting I'm missing to ensure proper CORS handling?
  3. Are there any known issues with CORS in Keycloak v26.0.7 or a better way to debug/prevent this issue?

Additional Context

Here's a snippet of the browser error:

Access to fetch at 'http://localhost:8090/realms/testrealm/protocol/openid-connect/auth' from origin 'http://localhost:3000' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.

Note

In my case, it is the backend server that acts as the Client in the OAuth 2.0 flow, not the frontend server.

This means that the backend server, not the frontend server, is the entity that makes authentication requests to the Keycloak authentication server.

The user only performs the authentication when requested to do so.

Flow

  1. :3000/test → Button click (request to the backend :8080/success-page)

  2. (302 redirect) → :8080/oauth2/authorization/keycloak

  3. (302 redirect) → :8090/realms/testrealm/protocol/openid-connect/auth/...

CORS Error Description

• The CORS error does not occur when redirecting from the frontend server to the Spring Boot backend server (:8080).

• However, the problem begins when redirecting from the backend server (:8080) to the Keycloak authentication server (:8090).

• During this step, the Origin header becomes null.

Keycloak detects the null origin and, as a result, does not include CORS-related headers in its response.

• When the browser receives the response from Keycloak without the required headers, it triggers a CORS error.

Share Improve this question edited Jan 2 at 7:13 June asked Jan 2 at 4:04 JuneJune 4361 gold badge7 silver badges18 bronze badges 5
  • 1 Async requests do not play nice when redirects are involved. When performing an authorisation flow, you are meant to navigate the client to the provider URL. – Phil Commented Jan 2 at 4:42
  • @Phil I don’t quite understand. Are you saying that the browser, after connecting to the frontend server, needs to know whether the user is authenticated before the user clicks any button (and before making any asynchronous API request to the backend server)? If the authentication status is determined only after making an asynchronous request to the backend, wouldn’t that happen after a CORS error has already occurred? Does that mean the authentication status needs to be managed independently on the frontend server? – June Commented Jan 2 at 5:26
  • 1 I'm not really sure what you're asking. Have a look at this demo repo. Note that to log in, the actual page is redirected to the Keycloak URL then redirected back to the app after authenticating – Phil Commented Jan 2 at 5:53
  • @Phil If I understood correctly, in this example, the Client in the OAuth 2.0 flow seems to be the frontend server (at :3000). For instance, even without running the backend server, I could enable authentication by having only the Keycloak and frontend server running, and then accessing localhost:3000. Please refer to the additional information added to the main content for more details – June Commented Jan 2 at 7:14
  • 1 Relevant (though there seems to be no preflight requests in your case): stackoverflow.com/questions/69494027/… – jub0bs Commented Jan 2 at 9:22
Add a comment  | 

1 Answer 1

Reset to default 1

As mentioned by @phil in his 1st comment, the redirection to the authorization server (Keycloak) should not be a cross-origin request, but a plain navigation.

First, a "smart" frontend consuming a REST API should answer 401 Unauthorized and not 302 redirect to login. For that, two options (the 1st is cleaner, especially if some resource servers allow anonymous access to some resources):

  • on the Gateway, permitAll() all requests routed with the TokenRelay filter and have the downstream resource server answer with 401 when authorization is required
  • configure Spring Security to answer 401 to anonymous requests on the routes with the TokenRelay filter

Second, theoretically, the frontend should redirect the user agent to the Gateway authorization initiation endpoint (by default /oauth2/authorization/{registration-id} in a Spring application with oauth2Login) with a plain navigation: set window.location.href, not use its framework HTTP client like Axios. If it doesn't, the Gateway should be configured to set a status in the 2xx range for the response with the location of the authorization server's authorization endpoint, so that the frontend code can read this answer and follow to that location by setting the window.location.href. This is what I did in this Baeldung article.

转载请注明原文地址:http://www.anycun.com/QandA/1746135126a92058.html