From bd494eb50b89f9d4c7ef51f76cbb9b71eb77a564 Mon Sep 17 00:00:00 2001 From: Geoffrey Fourmis Date: Fri, 19 Jan 2024 22:18:46 +0100 Subject: [PATCH] fix: Resolve HTTP 401 error on preflight requests due to CORS (#15) --- .../resource/AbstractAdminResource.java | 17 +++++++++++- .../multitenancy/resource/CorsResource.java | 26 +++++++++++++++++++ .../resource/TenantsResourceProvider.java | 8 +++++- 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 src/main/java/dev/sultanov/keycloak/multitenancy/resource/CorsResource.java diff --git a/src/main/java/dev/sultanov/keycloak/multitenancy/resource/AbstractAdminResource.java b/src/main/java/dev/sultanov/keycloak/multitenancy/resource/AbstractAdminResource.java index b057e1f..ae60242 100644 --- a/src/main/java/dev/sultanov/keycloak/multitenancy/resource/AbstractAdminResource.java +++ b/src/main/java/dev/sultanov/keycloak/multitenancy/resource/AbstractAdminResource.java @@ -10,6 +10,8 @@ import java.lang.reflect.Type; import org.keycloak.Config; import org.keycloak.connections.jpa.JpaConnectionProvider; +import org.keycloak.http.HttpRequest; +import org.keycloak.http.HttpResponse; import org.keycloak.jose.jws.JWSInput; import org.keycloak.jose.jws.JWSInputException; import org.keycloak.models.ClientModel; @@ -21,6 +23,7 @@ import org.keycloak.services.managers.AppAuthManager.BearerTokenAuthenticator; import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.RealmManager; +import org.keycloak.services.resources.Cors; import org.keycloak.services.resources.admin.AdminAuth; import org.keycloak.services.resources.admin.AdminEventBuilder; @@ -47,6 +50,18 @@ private void setup() { setupAuth(); setupEvents(); setupProvider(); + setupCors(); + } + + private void setupCors() { + HttpRequest request = session.getContext().getHttpRequest(); + HttpResponse response = session.getContext().getHttpResponse(); + Cors.add(request) + .allowedOrigins(auth.getToken()) + .allowedMethods(CorsResource.METHODS) + .exposedHeaders("Location") + .auth() + .build(response); } private void setupAuth() { @@ -120,4 +135,4 @@ protected final void setupProvider() { this.tenantProvider = session.getProvider(TenantProvider.class); } -} \ No newline at end of file +} diff --git a/src/main/java/dev/sultanov/keycloak/multitenancy/resource/CorsResource.java b/src/main/java/dev/sultanov/keycloak/multitenancy/resource/CorsResource.java new file mode 100644 index 0000000..a05d8fe --- /dev/null +++ b/src/main/java/dev/sultanov/keycloak/multitenancy/resource/CorsResource.java @@ -0,0 +1,26 @@ +package dev.sultanov.keycloak.multitenancy.resource; + +import jakarta.ws.rs.OPTIONS; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.core.Response; +import org.keycloak.http.HttpRequest; +import org.keycloak.services.resources.Cors; + +public class CorsResource { + + public static final String[] METHODS = { + "GET", "HEAD", "POST", "PUT", "DELETE", "PATCH", "OPTIONS" + }; + + private final HttpRequest request; + + public CorsResource(HttpRequest request) { + this.request = request; + } + + @OPTIONS + @Path("{any:.*}") + public Response preflight() { + return Cors.add(request, Response.ok()).auth().allowedMethods(METHODS).preflight().build(); + } +} diff --git a/src/main/java/dev/sultanov/keycloak/multitenancy/resource/TenantsResourceProvider.java b/src/main/java/dev/sultanov/keycloak/multitenancy/resource/TenantsResourceProvider.java index 84cdcb7..1273c2a 100644 --- a/src/main/java/dev/sultanov/keycloak/multitenancy/resource/TenantsResourceProvider.java +++ b/src/main/java/dev/sultanov/keycloak/multitenancy/resource/TenantsResourceProvider.java @@ -1,5 +1,6 @@ package dev.sultanov.keycloak.multitenancy.resource; +import org.keycloak.http.HttpRequest; import org.keycloak.models.KeycloakSession; import org.keycloak.services.resource.RealmResourceProvider; @@ -13,7 +14,12 @@ public TenantsResourceProvider(KeycloakSession session) { @Override public Object getResource() { - return new TenantsResource(session); + HttpRequest request = session.getContext().getHttpRequest(); + if (request != null && "OPTIONS".equals(request.getHttpMethod())) { + return new CorsResource(request); + } else { + return new TenantsResource(session); + } } @Override