From efd53f1d5d95ad0524aefdf9860ee7f760fbb8f7 Mon Sep 17 00:00:00 2001 From: Alexander Schwartz Date: Fri, 22 Dec 2023 12:36:48 +0100 Subject: [PATCH] Adding a test case to check that the expiration time is set on logout tokens Closes #25753 Signed-off-by: Alexander Schwartz (cherry picked from commit 9e890264dfa092028f9e71418728ff8d3b76d41b) --- .../jose/jws/DefaultTokenManager.java | 2 ++ .../keycloak/testsuite/oauth/LogoutTest.java | 24 ++++++++++++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/services/src/main/java/org/keycloak/jose/jws/DefaultTokenManager.java b/services/src/main/java/org/keycloak/jose/jws/DefaultTokenManager.java index 4fa4dc9e97c..9f0395a55f6 100644 --- a/services/src/main/java/org/keycloak/jose/jws/DefaultTokenManager.java +++ b/services/src/main/java/org/keycloak/jose/jws/DefaultTokenManager.java @@ -329,6 +329,8 @@ public class DefaultTokenManager implements TokenManager { LogoutToken token = new LogoutToken(); token.id(KeycloakModelUtils.generateId()); token.issuedNow(); + // From the spec "OpenID Connect Back-Channel Logout 1.0 incorporating errata set 1" at https://openid.net/specs/openid-connect-backchannel-1_0.html + // "OPs are encouraged to use short expiration times in Logout Tokens, preferably at most two minutes in the future [...]" token.exp(Time.currentTime() + Duration.ofMinutes(2).getSeconds()); token.issuer(clientSession.getNote(OIDCLoginProtocol.ISSUER)); token.putEvents(TokenUtil.TOKEN_BACKCHANNEL_LOGOUT_EVENT, JsonSerialization.createObjectNode()); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/LogoutTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/LogoutTest.java index b7cef62c9bd..e9f1fcc88f2 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/LogoutTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/LogoutTest.java @@ -17,6 +17,7 @@ package org.keycloak.testsuite.oauth; +import org.hamcrest.MatcherAssert; import org.jboss.arquillian.graphene.page.Page; import org.junit.Before; import org.junit.Rule; @@ -32,6 +33,7 @@ import org.keycloak.jose.jws.JWSHeader; import org.keycloak.jose.jws.JWSInput; import org.keycloak.protocol.oidc.OIDCConfigAttributes; import org.keycloak.protocol.oidc.OIDCLoginProtocol; +import org.keycloak.representations.LogoutToken; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.UserRepresentation; @@ -42,6 +44,8 @@ import org.keycloak.testsuite.admin.ApiUtil; import org.keycloak.testsuite.pages.LoginPage; import java.util.List; +import java.util.Map; + import jakarta.ws.rs.core.HttpHeaders; import jakarta.ws.rs.core.Response.Status; import jakarta.ws.rs.core.UriBuilder; @@ -328,13 +332,31 @@ public class LogoutTest extends AbstractKeycloakTest { assertThat(response.getFirstHeader(HttpHeaders.LOCATION).getValue(), is(oauth.APP_AUTH_ROOT)); } - assertNotNull(testingClient.testApp().getBackChannelLogoutToken()); + validateLogoutToken(testingClient.testApp().getBackChannelLogoutToken()); } finally { rep.getAttributes().put(OIDCConfigAttributes.BACKCHANNEL_LOGOUT_URL, ""); clientResource.update(rep); } } + /** + * Validate the token matches the spec at OpenID Connect Back-Channel Logout 1.0 incorporating errata set 1 + */ + private void validateLogoutToken(LogoutToken backChannelLogoutToken) { + assertNotNull("token must be present", backChannelLogoutToken); + assertNotNull("iss must be present", backChannelLogoutToken.getIssuer()); + assertNotNull("aud must be present", backChannelLogoutToken.getAudience()); + assertNotNull("iat must be present", backChannelLogoutToken.getIat()); + assertNotNull("exp must be present", backChannelLogoutToken.getExp()); + assertNotNull("jti must be present", backChannelLogoutToken.getId()); + Map events = backChannelLogoutToken.getEvents(); + assertNotNull("events must be present", events); + Object backchannelLogoutEvent = events.get("http://schemas.openid.net/event/backchannel-logout"); + assertNotNull("back-channel logout event must be present", backchannelLogoutEvent); + assertTrue("back-channel logout event must have a member object", backchannelLogoutEvent instanceof Map); + MatcherAssert.assertThat("map of back-channel logout event member object should be an empty object", (Map) backchannelLogoutEvent, org.hamcrest.Matchers.anEmptyMap()); + } + private OAuthClient.AccessTokenResponse loginAndForceNewLoginPage() { oauth.doLogin("test-user@localhost", "password");