Skip to content

Commit

Permalink
Changing locale on logout confirmation did not work
Browse files Browse the repository at this point in the history
  • Loading branch information
mposolda committed May 30, 2022
1 parent 4222de8 commit 26b80e4
Show file tree
Hide file tree
Showing 6 changed files with 274 additions and 48 deletions.
Expand Up @@ -494,6 +494,9 @@ protected void createCommonAttributes(Theme theme, Locale locale, Properties mes
case REGISTER:
b = UriBuilder.fromUri(Urls.realmRegisterPage(baseUri, realm.getName()));
break;
case LOGOUT_CONFIRM:
b = UriBuilder.fromUri(Urls.logoutConfirm(baseUri, realm.getName()));
break;
default:
b = UriBuilder.fromUri(baseUri).path(uriInfo.getPath());
break;
Expand Down
Expand Up @@ -38,6 +38,7 @@
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.SystemClientUtil;
import org.keycloak.protocol.oidc.BackchannelLogoutResponse;
Expand Down Expand Up @@ -65,6 +66,7 @@
import org.keycloak.services.resources.Cors;
import org.keycloak.services.resources.LogoutSessionCodeChecks;
import org.keycloak.services.resources.SessionCodeChecks;
import org.keycloak.services.util.LocaleUtil;
import org.keycloak.services.util.MtlsHoKTokenUtil;
import org.keycloak.sessions.AuthenticationSessionModel;
import org.keycloak.sessions.RootAuthenticationSessionModel;
Expand Down Expand Up @@ -258,9 +260,12 @@ public Response logout(@QueryParam(OIDCLoginProtocol.REDIRECT_URI_PARAM) String
LoginFormsProvider loginForm = session.getProvider(LoginFormsProvider.class)
.setAuthenticationSession(logoutSession);

UserSessionModel userSession = null;

// Check if we have session in the browser. If yes and it is different session than referenced by id_token_hint, the confirmation should be displayed
AuthenticationManager.AuthResult authResult = AuthenticationManager.authenticateIdentityCookie(session, realm, false);
if (authResult != null) {
userSession = authResult.getSession();
if (idToken != null && idToken.getSessionState() != null && !idToken.getSessionState().equals(authResult.getSession().getId())) {
forcedConfirmation = true;
}
Expand All @@ -272,6 +277,17 @@ public Response logout(@QueryParam(OIDCLoginProtocol.REDIRECT_URI_PARAM) String
}
}

if (userSession == null && idToken != null && idToken.getSessionState() != null) {
userSession = session.sessions().getUserSession(realm, idToken.getSessionState());
}

// Try to figure user because of localization
if (userSession != null) {
UserModel user = userSession.getUser();
logoutSession.setAuthenticatedUser(user);
loginForm.setUser(user);
}

// Logout confirmation screen will be displayed to the user in this case
if (confirmationNeeded || forcedConfirmation) {
return displayLogoutConfirmationScreen(loginForm, logoutSession);
Expand All @@ -297,6 +313,7 @@ private Response displayLogoutConfirmationScreen(LoginFormsProvider loginForm, A
* @return response
*/
@POST
@NoCache
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response logout() {
MultivaluedMap<String, String> form = request.getDecodedFormParameters();
Expand All @@ -315,6 +332,7 @@ public Response logout() {

@Path("/logout-confirm")
@POST
@NoCache
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response logoutConfirmAction() {
MultivaluedMap<String, String> formData = request.getDecodedFormParameters();
Expand All @@ -327,7 +345,7 @@ public Response logoutConfirmAction() {

SessionCodeChecks checks = new LogoutSessionCodeChecks(realm, session.getContext().getUri(), request, clientConnection, session, event, code, clientId, tabId);
checks.initialVerify();
if (!checks.verifyActiveAndValidAction(AuthenticationSessionModel.Action.LOGGING_OUT.name(), ClientSessionCode.ActionType.USER) || !formData.containsKey("confirmLogout")) {
if (!checks.verifyActiveAndValidAction(AuthenticationSessionModel.Action.LOGGING_OUT.name(), ClientSessionCode.ActionType.USER) || !checks.isActionRequest() || !formData.containsKey("confirmLogout")) {
AuthenticationSessionModel logoutSession = checks.getAuthenticationSession();
logger.debugf("Failed verification during logout. logoutSessionId=%s, clientId=%s, tabId=%s",
logoutSession != null ? logoutSession.getParentSession().getId() : "unknown", clientId, tabId);
Expand All @@ -347,6 +365,48 @@ public Response logoutConfirmAction() {
}


// Typically shown when user changes localization on the logout confirmation screen
@Path("/logout-confirm")
@NoCache
@GET
public Response logoutConfirmGet() {
event.event(EventType.LOGOUT);

String clientId = session.getContext().getUri().getQueryParameters().getFirst(Constants.CLIENT_ID);
String tabId = session.getContext().getUri().getQueryParameters().getFirst(Constants.TAB_ID);

logger.tracef("Changing localization by user during logout. clientId=%s, tabId=%s, kc_locale: %s", clientId, tabId, session.getContext().getUri().getQueryParameters().getFirst(LocaleSelectorProvider.KC_LOCALE_PARAM));

SessionCodeChecks checks = new LogoutSessionCodeChecks(realm, session.getContext().getUri(), request, clientConnection, session, event, null, clientId, tabId);
AuthenticationSessionModel logoutSession = checks.initialVerifyAuthSession();
if (logoutSession == null) {
logger.debugf("Failed verification when changing locale logout. clientId=%s, tabId=%s", clientId, tabId);

LoginFormsProvider loginForm = session.getProvider(LoginFormsProvider.class);
if (clientId == null || clientId.equals(SystemClientUtil.getSystemClient(realm).getClientId())) {
// Cleanup system client URL to avoid links to account management
loginForm.setAttribute(Constants.SKIP_LINK, true);
}

AuthenticationManager.AuthResult authResult = AuthenticationManager.authenticateIdentityCookie(session, realm, false);
if (authResult != null) {
return ErrorPage.error(session, logoutSession, Response.Status.BAD_REQUEST, Messages.FAILED_LOGOUT);
} else {
// Probably changing locale on logout screen after logout was already performed. If there is no session in the browser, we can just display that logout was already finished
return loginForm.setSuccess(Messages.SUCCESS_LOGOUT).createInfoPage();
}
}

LocaleUtil.processLocaleParam(session, realm, logoutSession);

LoginFormsProvider loginForm = session.getProvider(LoginFormsProvider.class)
.setAuthenticationSession(logoutSession)
.setUser(logoutSession.getAuthenticatedUser());

return displayLogoutConfirmationScreen(loginForm, logoutSession);
}


// Method triggered after user eventually confirmed that he wants to logout and all other checks were done
private Response doBrowserLogout(AuthenticationSessionModel logoutSession) {
UserSessionModel userSession = null;
Expand Down
Expand Up @@ -374,10 +374,10 @@ public static AuthenticationSessionModel createOrJoinLogoutSession(KeycloakSessi
} else {
logoutAuthSession = rootLogoutSession.createAuthenticationSession(client);
logoutAuthSession.setAction(AuthenticationSessionModel.Action.LOGGING_OUT.name());
session.getContext().setClient(client);
logger.tracef("Creating logout session for client '%s'. Authentication session id: %s", client.getClientId(), rootLogoutSession.getId());
}
session.getContext().setAuthenticationSession(logoutAuthSession);
session.getContext().setClient(client);

return logoutAuthSession;
}
Expand Down
Expand Up @@ -83,6 +83,7 @@
import org.keycloak.services.util.AuthenticationFlowURLHelper;
import org.keycloak.services.util.BrowserHistoryHelper;
import org.keycloak.services.util.CacheControlUtil;
import org.keycloak.services.util.LocaleUtil;
import org.keycloak.sessions.AuthenticationSessionCompoundId;
import org.keycloak.sessions.AuthenticationSessionModel;
import org.keycloak.sessions.RootAuthenticationSessionModel;
Expand Down Expand Up @@ -277,15 +278,7 @@ public Response authenticate(@QueryParam(AUTH_SESSION_ID) String authSessionId,
}

protected void processLocaleParam(AuthenticationSessionModel authSession) {
if (authSession != null && realm.isInternationalizationEnabled()) {
String locale = session.getContext().getUri().getQueryParameters().getFirst(LocaleSelectorProvider.KC_LOCALE_PARAM);
if (locale != null) {
authSession.setAuthNote(LocaleSelectorProvider.USER_REQUEST_LOCALE, locale);

LocaleUpdaterProvider localeUpdater = session.getProvider(LocaleUpdaterProvider.class);
localeUpdater.updateLocaleCookie(locale);
}
}
LocaleUtil.processLocaleParam(session, realm, authSession);
}

protected Response processAuthentication(boolean action, String execution, AuthenticationSessionModel authSession, String errorMessage) {
Expand Down
43 changes: 43 additions & 0 deletions services/src/main/java/org/keycloak/services/util/LocaleUtil.java
@@ -0,0 +1,43 @@
/*
* Copyright 2022 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package org.keycloak.services.util;

import org.keycloak.locale.LocaleSelectorProvider;
import org.keycloak.locale.LocaleUpdaterProvider;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.sessions.AuthenticationSessionModel;

/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class LocaleUtil {

public static void processLocaleParam(KeycloakSession session, RealmModel realm, AuthenticationSessionModel authSession) {
if (authSession != null && realm.isInternationalizationEnabled()) {
String locale = session.getContext().getUri().getQueryParameters().getFirst(LocaleSelectorProvider.KC_LOCALE_PARAM);
if (locale != null) {
authSession.setAuthNote(LocaleSelectorProvider.USER_REQUEST_LOCALE, locale);

LocaleUpdaterProvider localeUpdater = session.getProvider(LocaleUpdaterProvider.class);
localeUpdater.updateLocaleCookie(locale);
}
}
}
}

0 comments on commit 26b80e4

Please sign in to comment.