UPDATE_EMAIL action token handling should allow the user to resume its navigation to the redirect uri

Signed-off-by: Réda Housni Alaoui <reda-alaoui@hey.com>
This commit is contained in:
Réda Housni Alaoui 2024-02-08 17:42:35 +01:00 committed by Pedro Igor
parent b3321cb26e
commit 67718c653a
4 changed files with 33 additions and 3 deletions

View file

@ -30,17 +30,24 @@ public class UpdateEmailActionToken extends DefaultActionToken {
private String newEmail; private String newEmail;
@JsonProperty("logoutSessions") @JsonProperty("logoutSessions")
private Boolean logoutSessions; private Boolean logoutSessions;
@JsonProperty("reduri")
private String redirectUri;
public UpdateEmailActionToken(String userId, int absoluteExpirationInSecs, String oldEmail, String newEmail, String clientId) { public UpdateEmailActionToken(String userId, int absoluteExpirationInSecs, String oldEmail, String newEmail, String clientId) {
this(userId, absoluteExpirationInSecs, oldEmail, newEmail, clientId, null); this(userId, absoluteExpirationInSecs, oldEmail, newEmail, clientId, null);
} }
public UpdateEmailActionToken(String userId, int absoluteExpirationInSecs, String oldEmail, String newEmail, String clientId, Boolean logoutSessions){ public UpdateEmailActionToken(String userId, int absoluteExpirationInSecs, String oldEmail, String newEmail, String clientId, Boolean logoutSessions){
this(userId, absoluteExpirationInSecs, oldEmail, newEmail, clientId, logoutSessions, null);
}
public UpdateEmailActionToken(String userId, int absoluteExpirationInSecs, String oldEmail, String newEmail, String clientId, Boolean logoutSessions, String redirectUri){
super(userId, TOKEN_TYPE, absoluteExpirationInSecs, null); super(userId, TOKEN_TYPE, absoluteExpirationInSecs, null);
this.oldEmail = oldEmail; this.oldEmail = oldEmail;
this.newEmail = newEmail; this.newEmail = newEmail;
this.issuedFor = clientId; this.issuedFor = clientId;
this.logoutSessions = Boolean.TRUE.equals(logoutSessions)? true : null; this.logoutSessions = Boolean.TRUE.equals(logoutSessions)? true : null;
this.redirectUri = redirectUri;
} }
private UpdateEmailActionToken(){ private UpdateEmailActionToken(){
@ -70,4 +77,12 @@ public class UpdateEmailActionToken extends DefaultActionToken {
public void setLogoutSessions(Boolean logoutSessions) { public void setLogoutSessions(Boolean logoutSessions) {
this.logoutSessions = Boolean.TRUE.equals(logoutSessions)? true : null; this.logoutSessions = Boolean.TRUE.equals(logoutSessions)? true : null;
} }
public String getRedirectUri() {
return redirectUri;
}
public void setRedirectUri(String redirectUri) {
this.redirectUri = redirectUri;
}
} }

View file

@ -32,6 +32,9 @@ import org.keycloak.forms.login.LoginFormsProvider;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.models.utils.FormMessage; import org.keycloak.models.utils.FormMessage;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.utils.RedirectUtils;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.messages.Messages; import org.keycloak.services.messages.Messages;
import org.keycloak.services.validation.Validation; import org.keycloak.services.validation.Validation;
import org.keycloak.sessions.AuthenticationSessionModel; import org.keycloak.sessions.AuthenticationSessionModel;
@ -88,7 +91,12 @@ public class UpdateEmailActionTokenHandler extends AbstractActionTokenHandler<Up
user.removeRequiredAction(UserModel.RequiredAction.VERIFY_EMAIL); user.removeRequiredAction(UserModel.RequiredAction.VERIFY_EMAIL);
tokenContext.getAuthenticationSession().removeRequiredAction(UserModel.RequiredAction.VERIFY_EMAIL); tokenContext.getAuthenticationSession().removeRequiredAction(UserModel.RequiredAction.VERIFY_EMAIL);
return forms.setAttribute("messageHeader", forms.getMessage("emailUpdatedTitle")).setSuccess("emailUpdated", newEmail) AuthenticationSessionModel authSession = tokenContext.getAuthenticationSession();
String redirectUri = RedirectUtils.verifyRedirectUri(tokenContext.getSession(), token.getRedirectUri(), authSession.getClient());
return forms.setAttribute("messageHeader", forms.getMessage("emailUpdatedTitle"))
.setAttribute("pageRedirectUri", redirectUri)
.setSuccess("emailUpdated", newEmail)
.createInfoPage(); .createInfoPage();
} }

View file

@ -124,7 +124,7 @@ public class UpdateEmail implements RequiredActionProvider, RequiredActionFactor
AuthenticationSessionModel authenticationSession = context.getAuthenticationSession(); AuthenticationSessionModel authenticationSession = context.getAuthenticationSession();
UpdateEmailActionToken actionToken = new UpdateEmailActionToken(user.getId(), Time.currentTime() + validityInSecs, UpdateEmailActionToken actionToken = new UpdateEmailActionToken(user.getId(), Time.currentTime() + validityInSecs,
oldEmail, newEmail, authenticationSession.getClient().getClientId(), logoutSessions); oldEmail, newEmail, authenticationSession.getClient().getClientId(), logoutSessions, authenticationSession.getRedirectUri());
String link = Urls String link = Urls
.actionTokenBuilder(uriInfo.getBaseUri(), actionToken.serialize(session, realm, uriInfo), .actionTokenBuilder(uriInfo.getBaseUri(), actionToken.serialize(session, realm, uriInfo),

View file

@ -26,6 +26,7 @@ import jakarta.mail.MessagingException;
import jakarta.mail.internet.MimeMessage; import jakarta.mail.internet.MimeMessage;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.UUID;
import org.jboss.arquillian.graphene.page.Page; import org.jboss.arquillian.graphene.page.Page;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Rule; import org.junit.Rule;
@ -43,6 +44,7 @@ import org.keycloak.testsuite.pages.InfoPage;
import org.keycloak.testsuite.util.GreenMailRule; import org.keycloak.testsuite.util.GreenMailRule;
import org.keycloak.testsuite.util.MailUtils; import org.keycloak.testsuite.util.MailUtils;
import org.keycloak.testsuite.util.OAuthClient; import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.testsuite.util.WaitUtils;
public class RequiredActionUpdateEmailTestWithVerificationTest extends AbstractRequiredActionUpdateEmailTest { public class RequiredActionUpdateEmailTestWithVerificationTest extends AbstractRequiredActionUpdateEmailTest {
@ -66,6 +68,8 @@ public class RequiredActionUpdateEmailTestWithVerificationTest extends AbstractR
@Override @Override
protected void changeEmailUsingRequiredAction(String newEmail, boolean logoutOtherSessions) throws Exception { protected void changeEmailUsingRequiredAction(String newEmail, boolean logoutOtherSessions) throws Exception {
String redirectUri = OAuthClient.APP_ROOT + "/auth?nonce=" + UUID.randomUUID();
oauth.redirectUri(redirectUri);
loginPage.open(); loginPage.open();
loginPage.login("test-user@localhost", "password"); loginPage.login("test-user@localhost", "password");
@ -86,6 +90,9 @@ public class RequiredActionUpdateEmailTestWithVerificationTest extends AbstractR
infoPage.assertCurrent(); infoPage.assertCurrent();
assertEquals("The account email has been successfully updated to new@localhost.", infoPage.getInfo()); assertEquals("The account email has been successfully updated to new@localhost.", infoPage.getInfo());
infoPage.clickBackToApplicationLink();
WaitUtils.waitForPageToLoad();
assertEquals(redirectUri, driver.getCurrentUrl());
} }
private void updateEmail(boolean logoutOtherSessions) throws Exception { private void updateEmail(boolean logoutOtherSessions) throws Exception {