OIDC logout: In "legacy mode", support post_logout_redirect_uri param without requiring id_token_hint param
Closes #12680
This commit is contained in:
parent
ffc1265e9a
commit
b6d8c27cac
2 changed files with 56 additions and 23 deletions
|
@ -17,6 +17,10 @@
|
||||||
|
|
||||||
package org.keycloak.protocol.oidc.endpoints;
|
package org.keycloak.protocol.oidc.endpoints;
|
||||||
|
|
||||||
|
import static org.keycloak.models.UserSessionModel.State.LOGGED_OUT;
|
||||||
|
import static org.keycloak.models.UserSessionModel.State.LOGGING_OUT;
|
||||||
|
import static org.keycloak.services.resources.LoginActionsService.SESSION_CODE;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.jboss.resteasy.annotations.cache.NoCache;
|
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||||
import org.jboss.resteasy.spi.HttpRequest;
|
import org.jboss.resteasy.spi.HttpRequest;
|
||||||
|
@ -72,6 +76,10 @@ import org.keycloak.sessions.AuthenticationSessionModel;
|
||||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||||
import org.keycloak.util.TokenUtil;
|
import org.keycloak.util.TokenUtil;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import javax.ws.rs.Consumes;
|
import javax.ws.rs.Consumes;
|
||||||
import javax.ws.rs.GET;
|
import javax.ws.rs.GET;
|
||||||
import javax.ws.rs.OPTIONS;
|
import javax.ws.rs.OPTIONS;
|
||||||
|
@ -83,13 +91,6 @@ import javax.ws.rs.core.HttpHeaders;
|
||||||
import javax.ws.rs.core.MediaType;
|
import javax.ws.rs.core.MediaType;
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import static org.keycloak.models.UserSessionModel.State.LOGGED_OUT;
|
|
||||||
import static org.keycloak.models.UserSessionModel.State.LOGGING_OUT;
|
|
||||||
import static org.keycloak.services.resources.LoginActionsService.SESSION_CODE;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||||
|
@ -162,20 +163,27 @@ public class LogoutEndpoint {
|
||||||
@QueryParam(OIDCLoginProtocol.UI_LOCALES_PARAM) String uiLocales,
|
@QueryParam(OIDCLoginProtocol.UI_LOCALES_PARAM) String uiLocales,
|
||||||
@QueryParam(AuthenticationManager.INITIATING_IDP_PARAM) String initiatingIdp) {
|
@QueryParam(AuthenticationManager.INITIATING_IDP_PARAM) String initiatingIdp) {
|
||||||
|
|
||||||
if (deprecatedRedirectUri != null && !providerConfig.isLegacyLogoutRedirectUri()) {
|
if (!providerConfig.isLegacyLogoutRedirectUri()) {
|
||||||
event.event(EventType.LOGOUT);
|
if (deprecatedRedirectUri != null) {
|
||||||
event.error(Errors.INVALID_REQUEST);
|
event.event(EventType.LOGOUT);
|
||||||
logger.warnf("Parameter 'redirect_uri' no longer supported. Please use 'post_logout_redirect_uri' with 'id_token_hint' for this endpoint. Alternatively you can enable backwards compatibility option '%s' of oidc login protocol in the server configuration.",
|
event.error(Errors.INVALID_REQUEST);
|
||||||
OIDCLoginProtocolFactory.CONFIG_LEGACY_LOGOUT_REDIRECT_URI);
|
logger.warnf("Parameter 'redirect_uri' no longer supported. Please use 'post_logout_redirect_uri' with 'id_token_hint' for this endpoint. Alternatively you can enable backwards compatibility option '%s' of oidc login protocol in the server configuration.",
|
||||||
return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.INVALID_PARAMETER, OIDCLoginProtocol.REDIRECT_URI_PARAM);
|
OIDCLoginProtocolFactory.CONFIG_LEGACY_LOGOUT_REDIRECT_URI);
|
||||||
|
return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.INVALID_PARAMETER, OIDCLoginProtocol.REDIRECT_URI_PARAM);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (postLogoutRedirectUri != null && encodedIdToken == null && clientId == null) {
|
||||||
|
event.event(EventType.LOGOUT);
|
||||||
|
event.error(Errors.INVALID_REQUEST);
|
||||||
|
logger.warnf(
|
||||||
|
"Either the parameter 'client_id' or the parameter 'id_token_hint' is required when 'post_logout_redirect_uri' is used.");
|
||||||
|
return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.MISSING_PARAMETER,
|
||||||
|
OIDCLoginProtocol.ID_TOKEN_HINT);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (postLogoutRedirectUri != null && encodedIdToken == null && clientId == null) {
|
deprecatedRedirectUri = providerConfig.isLegacyLogoutRedirectUri() ? deprecatedRedirectUri : null;
|
||||||
event.event(EventType.LOGOUT);
|
final String redirectUri = postLogoutRedirectUri != null ? postLogoutRedirectUri : deprecatedRedirectUri;
|
||||||
event.error(Errors.INVALID_REQUEST);
|
|
||||||
logger.warnf("Either the parameter 'client_id' or the parameter 'id_token_hint' is required when 'post_logout_redirect_uri' is used.");
|
|
||||||
return ErrorPage.error(session, null, Response.Status.BAD_REQUEST, Messages.MISSING_PARAMETER, OIDCLoginProtocol.ID_TOKEN_HINT);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean confirmationNeeded = true;
|
boolean confirmationNeeded = true;
|
||||||
boolean forcedConfirmation = false;
|
boolean forcedConfirmation = false;
|
||||||
|
@ -222,12 +230,15 @@ public class LogoutEndpoint {
|
||||||
}
|
}
|
||||||
|
|
||||||
String validatedRedirectUri = null;
|
String validatedRedirectUri = null;
|
||||||
if (postLogoutRedirectUri != null || deprecatedRedirectUri != null) {
|
if (redirectUri != null) {
|
||||||
String redirectUri = postLogoutRedirectUri != null ? postLogoutRedirectUri : deprecatedRedirectUri;
|
|
||||||
if (client != null) {
|
if (client != null) {
|
||||||
validatedRedirectUri = RedirectUtils.verifyRedirectUri(session, redirectUri, client);
|
validatedRedirectUri = RedirectUtils.verifyRedirectUri(session, redirectUri, client);
|
||||||
} else if (providerConfig.isLegacyLogoutRedirectUri()) {
|
} else if (clientId == null) {
|
||||||
validatedRedirectUri = RedirectUtils.verifyRealmRedirectUri(session, deprecatedRedirectUri);
|
/*
|
||||||
|
* Only call verifyRealmRedirectUri, in case both clientId and client are null - otherwise
|
||||||
|
* the logout uri contains a non-existing client, and we should show an INVALID_REDIRECT_URI error
|
||||||
|
*/
|
||||||
|
validatedRedirectUri = RedirectUtils.verifyRealmRedirectUri(session, redirectUri);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (validatedRedirectUri == null) {
|
if (validatedRedirectUri == null) {
|
||||||
|
|
|
@ -52,6 +52,7 @@ import org.keycloak.testsuite.util.InfinispanTestTimeServiceRule;
|
||||||
import org.keycloak.testsuite.util.OAuthClient;
|
import org.keycloak.testsuite.util.OAuthClient;
|
||||||
import org.keycloak.testsuite.util.ServerURLs;
|
import org.keycloak.testsuite.util.ServerURLs;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlDoesntStartWith;
|
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlDoesntStartWith;
|
||||||
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals;
|
import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals;
|
||||||
|
@ -147,6 +148,27 @@ public class LegacyLogoutTest extends AbstractTestRealmKeycloakTest {
|
||||||
assertCurrentUrlEquals(APP_REDIRECT_URI);
|
assertCurrentUrlEquals(APP_REDIRECT_URI);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Test with "post_logout_redirect_uri" without "id_token_hint": User should confirm logout.
|
||||||
|
@Test
|
||||||
|
public void logoutWithPostLogoutUriWithoutIdTokenHint() {
|
||||||
|
OAuthClient.AccessTokenResponse tokenResponse = loginUser();
|
||||||
|
String sessionId = tokenResponse.getSessionState();
|
||||||
|
|
||||||
|
String logoutUrl = oauth.getLogoutUrl().postLogoutRedirectUri(APP_REDIRECT_URI).build();
|
||||||
|
driver.navigate().to(logoutUrl);
|
||||||
|
|
||||||
|
// Assert logout confirmation page. Session still exists. Assert default language on logout page (English)
|
||||||
|
logoutConfirmPage.assertCurrent();
|
||||||
|
assertThat(true, is(isSessionActive(sessionId)));
|
||||||
|
events.assertEmpty();
|
||||||
|
logoutConfirmPage.confirmLogout();
|
||||||
|
|
||||||
|
// Redirected back to the application with expected state
|
||||||
|
events.expectLogout(sessionId).removeDetail(Details.REDIRECT_URI).assertEvent();
|
||||||
|
assertThat(false, is(isSessionActive(sessionId)));
|
||||||
|
assertCurrentUrlEquals(APP_REDIRECT_URI);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// KEYCLOAK-16517 Make sure that just real clients with standardFlow or implicitFlow enabled are considered for redirectUri
|
// KEYCLOAK-16517 Make sure that just real clients with standardFlow or implicitFlow enabled are considered for redirectUri
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in a new issue