From 7153bdd70bfe403489d67ce63793ff50c48eaa17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Kr=C3=A1l?= Date: Thu, 9 Feb 2023 00:31:28 +0100 Subject: [PATCH] OIDC logout functionality fixed (#6118) Signed-off-by: David Kral --- .../security/providers/oidc/OidcSupport.java | 4 +- .../tests/integration/oidc/TestResource.java | 14 ++++++- .../integration/oidc/CookieBasedLoginIT.java | 39 +++++++++++++++++++ .../oidc/src/test/resources/application.yaml | 4 +- 4 files changed, 58 insertions(+), 3 deletions(-) diff --git a/security/providers/oidc/src/main/java/io/helidon/security/providers/oidc/OidcSupport.java b/security/providers/oidc/src/main/java/io/helidon/security/providers/oidc/OidcSupport.java index 3c9591afa9a..3a1a57b20c3 100644 --- a/security/providers/oidc/src/main/java/io/helidon/security/providers/oidc/OidcSupport.java +++ b/security/providers/oidc/src/main/java/io/helidon/security/providers/oidc/OidcSupport.java @@ -240,6 +240,7 @@ private void processTenantLogout(ServerRequest req, ServerResponse res, String t private void logoutWithTenant(ServerRequest req, ServerResponse res, Tenant tenant) { OidcCookieHandler idTokenCookieHandler = oidcConfig.idTokenCookieHandler(); OidcCookieHandler tokenCookieHandler = oidcConfig.tokenCookieHandler(); + OidcCookieHandler tenantCookieHandler = oidcConfig.tenantCookieHandler(); Optional idTokenCookie = req.headers() .cookies() @@ -267,6 +268,7 @@ private void logoutWithTenant(ServerRequest req, ServerResponse res, Tenant tena ResponseHeaders headers = res.headers(); headers.addCookie(tokenCookieHandler.removeCookie().build()); headers.addCookie(idTokenCookieHandler.removeCookie().build()); + headers.addCookie(tenantCookieHandler.removeCookie().build()); res.status(Http.Status.TEMPORARY_REDIRECT_307) .addHeader(Http.Header.LOCATION, sb.toString()) @@ -452,7 +454,7 @@ private String processJsonResponse(ServerRequest req, .forSingle(builder -> { headers.addCookie(builder.build()); if (idToken != null && oidcConfig.logoutEnabled()) { - tokenCookieHandler.createCookie(idToken) + oidcConfig.idTokenCookieHandler().createCookie(idToken) .forSingle(it -> { headers.addCookie(it.build()); res.send(); diff --git a/tests/integration/oidc/src/main/java/io/helidon/tests/integration/oidc/TestResource.java b/tests/integration/oidc/src/main/java/io/helidon/tests/integration/oidc/TestResource.java index 328a62f01a2..fa6924f8a65 100644 --- a/tests/integration/oidc/src/main/java/io/helidon/tests/integration/oidc/TestResource.java +++ b/tests/integration/oidc/src/main/java/io/helidon/tests/integration/oidc/TestResource.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Oracle and/or its affiliates. + * Copyright (c) 2022, 2023 Oracle and/or its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,8 @@ import jakarta.ws.rs.GET; import jakarta.ws.rs.Path; import jakarta.ws.rs.Produces; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.MediaType; import io.helidon.security.annotations.Authenticated; @@ -32,6 +34,7 @@ public class TestResource { public static final String EXPECTED_TEST_MESSAGE = "Hello world"; + public static final String EXPECTED_POST_LOGOUT_TEST_MESSAGE = "Post logout endpoint reached with no cookies"; /** * Return hello world message. @@ -45,5 +48,14 @@ public String getDefaultMessage() { return EXPECTED_TEST_MESSAGE; } + @Path("/postLogout") + @GET + @Produces(MediaType.TEXT_PLAIN) + public String postLogout(@Context HttpHeaders httpHeaders) { + if (httpHeaders.getCookies().isEmpty()) { + return EXPECTED_POST_LOGOUT_TEST_MESSAGE; + } + return "Cookies are not cleared!"; + } } diff --git a/tests/integration/oidc/src/test/java/io/helidon/tests/integration/oidc/CookieBasedLoginIT.java b/tests/integration/oidc/src/test/java/io/helidon/tests/integration/oidc/CookieBasedLoginIT.java index b5e49c14aea..0d528f2b57e 100644 --- a/tests/integration/oidc/src/test/java/io/helidon/tests/integration/oidc/CookieBasedLoginIT.java +++ b/tests/integration/oidc/src/test/java/io/helidon/tests/integration/oidc/CookieBasedLoginIT.java @@ -24,6 +24,7 @@ import org.jsoup.nodes.Document; import org.junit.jupiter.api.Test; +import static io.helidon.tests.integration.oidc.TestResource.EXPECTED_POST_LOGOUT_TEST_MESSAGE; import static io.helidon.tests.integration.oidc.TestResource.EXPECTED_TEST_MESSAGE; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.not; @@ -124,6 +125,44 @@ public void testDefaultTenantUsage(WebTarget webTarget) { } } + + @Test + public void testLogoutFunctionality(WebTarget webTarget) { + String formUri; + + //greet endpoint is protected, and we need to get JWT token out of the Keycloak. We will get redirected to the Keycloak. + try (Response response = client.target(webTarget.getUri()).path("/test") + .request() + .get()) { + assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode())); + //We need to get form URI out of the HTML + formUri = getRequestUri(response.readEntity(String.class)); + } + + //Sending authentication to the Keycloak and getting redirected back to the running Helidon app. + Entity
form = Entity.form(new Form().param("username", "userone") + .param("password", "12345") + .param("credentialId", "")); + try (Response response = client.target(formUri).request().post(form)) { + assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode())); + assertThat(response.readEntity(String.class), is(EXPECTED_TEST_MESSAGE)); + } + + try (Response response = client.target(webTarget.getUri()).path("/oidc/logout") + .request() + .get()) { + assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode())); + assertThat(response.readEntity(String.class), is(EXPECTED_POST_LOGOUT_TEST_MESSAGE)); + } + + try (Response response = client.target(webTarget.getUri()).path("/oidc/logout") + .request() + .get()) { + //There should be not token present among the cookies since it was cleared by the previous call + assertThat(response.getStatus(), is(Response.Status.FORBIDDEN.getStatusCode())); + } + } + private String getRequestUri(String html) { Document document = Jsoup.parse(html); return document.getElementById("kc-form-login").attr("action"); diff --git a/tests/integration/oidc/src/test/resources/application.yaml b/tests/integration/oidc/src/test/resources/application.yaml index f1925c76d27..619489620ee 100644 --- a/tests/integration/oidc/src/test/resources/application.yaml +++ b/tests/integration/oidc/src/test/resources/application.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2022 Oracle and/or its affiliates. +# Copyright (c) 2022, 2023 Oracle and/or its affiliates. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -31,6 +31,8 @@ security: redirect-uri: "/oidc/redirect" audience: "account" header-use: true + logout-enabled: true + post-logout-uri: "/test/postLogout" client-id: "clientOne" client-secret: "F5s4VBtMJF3SMdiIRkLEXioM9UPf34OR" identity-uri: "http://localhost:8080/realms/test/"