Skip to content

Commit 1213006

Browse files
authored
Merge pull request quarkusio#35919 from sberyozkin/oidc_logout_with_expired_idtoken
Honor OIDC logout requests when ID token has expired
2 parents 237f7be + 737d27f commit 1213006

File tree

5 files changed

+41
-1
lines changed

5 files changed

+41
-1
lines changed

extensions/oidc/runtime/src/main/java/io/quarkus/oidc/SecurityEvent.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
*
1010
*/
1111
public class SecurityEvent {
12+
public static final String SESSION_TOKENS_PROPERTY = "session-tokens";
13+
1214
public enum Type {
1315
/**
1416
* OIDC Login event which is reported after the first user authentication but also when the user's session
@@ -30,6 +32,12 @@ public enum Type {
3032
*/
3133
OIDC_LOGOUT_RP_INITIATED,
3234

35+
/**
36+
* OIDC Logout event is reported when the current user has started an RP-initiated OIDC logout flow but the session has
37+
* already expired.
38+
*/
39+
OIDC_LOGOUT_RP_INITIATED_SESSION_EXPIRED,
40+
3341
/**
3442
* OIDC BackChannel Logout initiated event is reported when the BackChannel logout request to logout the current user
3543
* has been received.

extensions/oidc/runtime/src/main/java/io/quarkus/oidc/runtime/CodeAuthenticationMechanism.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,14 @@ public Uni<? extends SecurityIdentity> apply(Throwable t) {
354354
t.getCause())));
355355
}
356356
// Token has expired, try to refresh
357+
if (isRpInitiatedLogout(context, configContext)) {
358+
LOG.debug("Session has expired, performing an RP initiated logout");
359+
fireEvent(SecurityEvent.Type.OIDC_LOGOUT_RP_INITIATED_SESSION_EXPIRED,
360+
Map.of(SecurityEvent.SESSION_TOKENS_PROPERTY, session));
361+
return Uni.createFrom().item((SecurityIdentity) null)
362+
.call(() -> buildLogoutRedirectUriUni(context, configContext,
363+
currentIdToken));
364+
}
357365
if (session.getRefreshToken() == null) {
358366
LOG.debug(
359367
"Token has expired, token refresh is not possible because the refresh token is null");
@@ -983,6 +991,12 @@ private void fireEvent(SecurityEvent.Type eventType, SecurityIdentity securityId
983991
}
984992
}
985993

994+
private void fireEvent(SecurityEvent.Type eventType, Map<String, Object> properties) {
995+
if (resolver.isSecurityEventObserved()) {
996+
resolver.getSecurityEvent().fire(new SecurityEvent(eventType, properties));
997+
}
998+
}
999+
9861000
private String getRedirectPath(OidcTenantConfig oidcConfig, RoutingContext context) {
9871001
Authentication auth = oidcConfig.getAuthentication();
9881002
return auth.getRedirectPath().isPresent() ? auth.getRedirectPath().get() : context.request().path();

integration-tests/oidc-code-flow/src/main/resources/application.properties

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ quarkus.oidc.tenant-logout.application-type=web-app
8080
quarkus.oidc.tenant-logout.authentication.cookie-path=/tenant-logout
8181
quarkus.oidc.tenant-logout.logout.path=/tenant-logout/logout
8282
quarkus.oidc.tenant-logout.logout.post-logout-path=/tenant-logout/post-logout
83+
quarkus.oidc.tenant-logout.authentication.session-age-extension=2M
84+
quarkus.oidc.tenant-logout.token.refresh-expired=true
8385

8486
quarkus.oidc.tenant-refresh.auth-server-url=${keycloak.url}/realms/logout-realm
8587
quarkus.oidc.tenant-refresh.client-id=quarkus-app

integration-tests/oidc-code-flow/src/test/java/io/quarkus/it/keycloak/CodeFlowTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,22 @@ public void testRPInitiatedLogout() throws IOException {
611611

612612
page = webClient.getPage("http://localhost:8081/tenant-logout");
613613
assertEquals("Sign in to logout-realm", page.getTitleText());
614+
615+
// login again
616+
loginForm = page.getForms().get(0);
617+
loginForm.getInputByName("username").setValueAttribute("alice");
618+
loginForm.getInputByName("password").setValueAttribute("alice");
619+
page = loginForm.getInputByName("login").click();
620+
assertEquals("Tenant Logout, refreshed: false", page.asNormalizedText());
621+
622+
assertNotNull(getSessionCookie(webClient, "tenant-logout"));
623+
624+
await().atLeast(Duration.ofSeconds(11));
625+
626+
page = webClient.getPage("http://localhost:8081/tenant-logout/logout");
627+
assertTrue(page.asNormalizedText().contains("You were logged out, please login again"));
628+
assertNull(getSessionCookie(webClient, "tenant-logout"));
629+
614630
webClient.getCookieManager().clearCookies();
615631
}
616632
}

integration-tests/oidc-code-flow/src/test/java/io/quarkus/it/keycloak/KeycloakRealmResourceManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ public Map<String, String> start() {
3535
// revoke refresh tokens so that they can only be used once
3636
logoutRealm.setRevokeRefreshToken(true);
3737
logoutRealm.setRefreshTokenMaxReuse(0);
38-
logoutRealm.setSsoSessionMaxLifespan(15);
38+
logoutRealm.setSsoSessionMaxLifespan(10);
3939
logoutRealm.setAccessTokenLifespan(5);
4040
client.createRealm(logoutRealm);
4141
realms.add(logoutRealm);

0 commit comments

Comments
 (0)