keycloak-uncached
Changes
services/src/main/java/org/keycloak/authentication/actiontoken/idpverifyemail/IdpVerifyAccountLinkActionToken.java 3(+2 -1)
services/src/main/java/org/keycloak/authentication/actiontoken/resetcred/ResetCredentialsActionToken.java 3(+2 -1)
services/src/main/java/org/keycloak/authentication/actiontoken/verifyemail/VerifyEmailActionToken.java 3(+2 -1)
services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpEmailVerificationAuthenticator.java 2(+1 -1)
services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialEmail.java 2(+1 -1)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java 41(+38 -3)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBaseBrokerTest.java 19(+19 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBrokerTest.java 103(+94 -9)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerConfiguration.java 15(+14 -1)
Details
diff --git a/services/src/main/java/org/keycloak/authentication/actiontoken/idpverifyemail/IdpVerifyAccountLinkActionToken.java b/services/src/main/java/org/keycloak/authentication/actiontoken/idpverifyemail/IdpVerifyAccountLinkActionToken.java
index 4d6b551..004f88b 100644
--- a/services/src/main/java/org/keycloak/authentication/actiontoken/idpverifyemail/IdpVerifyAccountLinkActionToken.java
+++ b/services/src/main/java/org/keycloak/authentication/actiontoken/idpverifyemail/IdpVerifyAccountLinkActionToken.java
@@ -43,10 +43,11 @@ public class IdpVerifyAccountLinkActionToken extends DefaultActionToken {
public IdpVerifyAccountLinkActionToken(String userId, int absoluteExpirationInSecs, String compoundAuthenticationSessionId,
- String identityProviderUsername, String identityProviderAlias) {
+ String identityProviderUsername, String identityProviderAlias, String clientId) {
super(userId, TOKEN_TYPE, absoluteExpirationInSecs, null, compoundAuthenticationSessionId);
this.identityProviderUsername = identityProviderUsername;
this.identityProviderAlias = identityProviderAlias;
+ this.issuedFor = clientId;
}
private IdpVerifyAccountLinkActionToken() {
diff --git a/services/src/main/java/org/keycloak/authentication/actiontoken/resetcred/ResetCredentialsActionToken.java b/services/src/main/java/org/keycloak/authentication/actiontoken/resetcred/ResetCredentialsActionToken.java
index 5776617..bb7969c 100644
--- a/services/src/main/java/org/keycloak/authentication/actiontoken/resetcred/ResetCredentialsActionToken.java
+++ b/services/src/main/java/org/keycloak/authentication/actiontoken/resetcred/ResetCredentialsActionToken.java
@@ -27,8 +27,9 @@ public class ResetCredentialsActionToken extends DefaultActionToken {
public static final String TOKEN_TYPE = "reset-credentials";
- public ResetCredentialsActionToken(String userId, int absoluteExpirationInSecs, String compoundAuthenticationSessionId) {
+ public ResetCredentialsActionToken(String userId, int absoluteExpirationInSecs, String compoundAuthenticationSessionId, String clientId) {
super(userId, TOKEN_TYPE, absoluteExpirationInSecs, null, compoundAuthenticationSessionId);
+ this.issuedFor = clientId;
}
private ResetCredentialsActionToken() {
diff --git a/services/src/main/java/org/keycloak/authentication/actiontoken/verifyemail/VerifyEmailActionToken.java b/services/src/main/java/org/keycloak/authentication/actiontoken/verifyemail/VerifyEmailActionToken.java
index 2ccd571..4d0d5a5 100644
--- a/services/src/main/java/org/keycloak/authentication/actiontoken/verifyemail/VerifyEmailActionToken.java
+++ b/services/src/main/java/org/keycloak/authentication/actiontoken/verifyemail/VerifyEmailActionToken.java
@@ -37,9 +37,10 @@ public class VerifyEmailActionToken extends DefaultActionToken {
@JsonProperty(value = JSON_FIELD_ORIGINAL_AUTHENTICATION_SESSION_ID)
private String originalAuthenticationSessionId;
- public VerifyEmailActionToken(String userId, int absoluteExpirationInSecs, String compoundAuthenticationSessionId, String email) {
+ public VerifyEmailActionToken(String userId, int absoluteExpirationInSecs, String compoundAuthenticationSessionId, String email, String clientId) {
super(userId, TOKEN_TYPE, absoluteExpirationInSecs, null, compoundAuthenticationSessionId);
this.email = email;
+ this.issuedFor = clientId;
}
private VerifyEmailActionToken() {
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpEmailVerificationAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpEmailVerificationAuthenticator.java
index 3634d3c..fa8bdea 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpEmailVerificationAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/broker/IdpEmailVerificationAuthenticator.java
@@ -131,7 +131,7 @@ public class IdpEmailVerificationAuthenticator extends AbstractIdpAuthenticator
String authSessionEncodedId = AuthenticationSessionCompoundId.fromAuthSession(authSession).getEncodedId();
IdpVerifyAccountLinkActionToken token = new IdpVerifyAccountLinkActionToken(
existingUser.getId(), absoluteExpirationInSecs, authSessionEncodedId,
- brokerContext.getUsername(), brokerContext.getIdpConfig().getAlias()
+ brokerContext.getUsername(), brokerContext.getIdpConfig().getAlias(), authSession.getClient().getClientId()
);
UriBuilder builder = Urls.actionTokenBuilder(uriInfo.getBaseUri(), token.serialize(session, realm, uriInfo),
authSession.getClient().getClientId(), authSession.getTabId());
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialEmail.java b/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialEmail.java
index e4befe2..4fb8826 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialEmail.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/resetcred/ResetCredentialEmail.java
@@ -91,7 +91,7 @@ public class ResetCredentialEmail implements Authenticator, AuthenticatorFactory
// We send the secret in the email in a link as a query param.
String authSessionEncodedId = AuthenticationSessionCompoundId.fromAuthSession(authenticationSession).getEncodedId();
- ResetCredentialsActionToken token = new ResetCredentialsActionToken(user.getId(), absoluteExpirationInSecs, authSessionEncodedId);
+ ResetCredentialsActionToken token = new ResetCredentialsActionToken(user.getId(), absoluteExpirationInSecs, authSessionEncodedId, authenticationSession.getClient().getClientId());
String link = UriBuilder
.fromUri(context.getActionTokenUrl(token.serialize(context.getSession(), context.getRealm(), context.getUriInfo())))
.build()
diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/VerifyEmail.java b/services/src/main/java/org/keycloak/authentication/requiredactions/VerifyEmail.java
index c29c616..876dcd4 100755
--- a/services/src/main/java/org/keycloak/authentication/requiredactions/VerifyEmail.java
+++ b/services/src/main/java/org/keycloak/authentication/requiredactions/VerifyEmail.java
@@ -143,7 +143,7 @@ public class VerifyEmail implements RequiredActionProvider, RequiredActionFactor
int absoluteExpirationInSecs = Time.currentTime() + validityInSecs;
String authSessionEncodedId = AuthenticationSessionCompoundId.fromAuthSession(authSession).getEncodedId();
- VerifyEmailActionToken token = new VerifyEmailActionToken(user.getId(), absoluteExpirationInSecs, authSessionEncodedId, user.getEmail());
+ VerifyEmailActionToken token = new VerifyEmailActionToken(user.getId(), absoluteExpirationInSecs, authSessionEncodedId, user.getEmail(), authSession.getClient().getClientId());
UriBuilder builder = Urls.actionTokenBuilder(uriInfo.getBaseUri(), token.serialize(session, realm, uriInfo),
authSession.getClient().getClientId(), authSession.getTabId());
String link = builder.build(realm.getName()).toString();
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java
index ca94437..83c294c 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/actions/RequiredActionEmailVerificationTest.java
@@ -17,6 +17,7 @@
package org.keycloak.testsuite.actions;
import org.jboss.arquillian.drone.api.annotation.Drone;
+import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.authentication.actiontoken.verifyemail.VerifyEmailActionToken;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Assert;
@@ -29,6 +30,7 @@ import org.keycloak.events.Errors;
import org.keycloak.events.EventType;
import org.keycloak.models.Constants;
import org.keycloak.models.UserModel.RequiredAction;
+import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.EventRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
@@ -43,9 +45,11 @@ import org.keycloak.testsuite.pages.InfoPage;
import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.pages.RegisterPage;
import org.keycloak.testsuite.pages.VerifyEmailPage;
+import org.keycloak.testsuite.updaters.ClientAttributeUpdater;
import org.keycloak.testsuite.updaters.UserAttributeUpdater;
import org.keycloak.testsuite.util.GreenMailRule;
import org.keycloak.testsuite.util.MailUtils;
+import org.keycloak.testsuite.util.RealmBuilder;
import org.keycloak.testsuite.util.SecondBrowser;
import org.keycloak.testsuite.util.UserActionTokenBuilder;
import org.keycloak.testsuite.util.UserBuilder;
@@ -384,9 +388,7 @@ public class RequiredActionEmailVerificationTest extends AbstractTestRealmKeyclo
events.expectRequiredAction(EventType.VERIFY_EMAIL)
.user(testUserId)
.detail(Details.CODE_ID, Matchers.not(Matchers.is(mailCodeId)))
- .client(Constants.ACCOUNT_MANAGEMENT_CLIENT_ID) // as authentication sessions are browser-specific,
- // the client and redirect_uri is unrelated to
- // the "test-app" specified in loginPage.open()
+ .client(oauth.getClientId()) // the "test-app" client specified in loginPage.open() is expected
.detail(Details.REDIRECT_URI, Matchers.any(String.class))
.assertEvent();
@@ -630,6 +632,39 @@ public class RequiredActionEmailVerificationTest extends AbstractTestRealmKeyclo
}
@Test
+ public void verifyEmailNewBrowserSessionPreserveClient() throws IOException, MessagingException {
+ loginPage.open();
+ loginPage.login("test-user@localhost", "password");
+
+ verifyEmailPage.assertCurrent();
+
+ Assert.assertEquals(1, greenMail.getReceivedMessages().length);
+
+ MimeMessage message = greenMail.getLastReceivedMessage();
+
+ String verificationUrl = getPasswordResetEmailLink(message);
+
+ // open link in the second browser without the session
+ driver2.navigate().to(verificationUrl.trim());
+
+ // follow the link
+ final WebElement proceedLink = driver2.findElement(By.linkText("» Click here to proceed"));
+ assertThat(proceedLink, Matchers.notNullValue());
+
+ // check if the initial client is preserved
+ String link = proceedLink.getAttribute("href");
+ assertThat(link, Matchers.containsString("client_id=test-app"));
+ proceedLink.click();
+
+ // confirmation in the second browser
+ assertThat(driver2.getPageSource(), Matchers.containsString("kc-info-message"));
+ assertThat(driver2.getPageSource(), Matchers.containsString("Your email address has been verified."));
+
+ final WebElement backToApplicationLink = driver2.findElement(By.linkText("« Back to Application"));
+ assertThat(backToApplicationLink, Matchers.notNullValue());
+ }
+
+ @Test
public void verifyEmailDuringAuthFlow() throws IOException, MessagingException {
try (Closeable u = new UserAttributeUpdater(testRealm().users().get(testUserId))
.setEmailVerified(false)
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBaseBrokerTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBaseBrokerTest.java
index 09f289f..f80a436 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBaseBrokerTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBaseBrokerTest.java
@@ -19,9 +19,11 @@ package org.keycloak.testsuite.broker;
import java.util.List;
+import org.hamcrest.Matchers;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.After;
import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testsuite.AbstractKeycloakTest;
@@ -36,6 +38,7 @@ import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.pages.UpdateAccountInformationPage;
import org.openqa.selenium.TimeoutException;
+import static org.junit.Assert.assertThat;
import static org.keycloak.testsuite.broker.BrokerTestTools.encodeUrl;
import static org.keycloak.testsuite.broker.BrokerTestTools.waitForPage;
@@ -142,6 +145,22 @@ public abstract class AbstractBaseBrokerTest extends AbstractKeycloakTest {
return BrokerTestTools.getAuthRoot(suiteContext) + "/auth/realms/" + realmName + "/account/password";
}
+ /**
+ * Get the login page for an existing client in provided realm
+ * @param realmName Name of the realm
+ * @param clientId ClientId of a client. Client has to exists in the realm.
+ * @return Login URL
+ */
+ protected String getLoginUrl(String realmName, String clientId) {
+ List<ClientRepresentation> clients = adminClient.realm(realmName).clients().findByClientId(clientId);
+
+ assertThat(clients, Matchers.is(Matchers.not(Matchers.empty())));
+
+ String redirectURI = clients.get(0).getBaseUrl();
+
+ return BrokerTestTools.getAuthRoot(suiteContext) + "/auth/realms/" + realmName + "/protocol/openid-connect/auth?client_id=" +
+ clientId + "&redirect_uri=" + redirectURI + "&response_type=code&scope=openid";
+ }
protected void logoutFromRealm(String realm) {
driver.navigate().to(BrokerTestTools.getAuthRoot(suiteContext)
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBrokerTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBrokerTest.java
index f9f141d..8aaaf0b 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBrokerTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractBrokerTest.java
@@ -1,5 +1,8 @@
package org.keycloak.testsuite.broker;
+import org.hamcrest.MatcherAssert;
+import org.hamcrest.Matchers;
+import org.jboss.arquillian.drone.api.annotation.Drone;
import org.junit.Before;
import org.junit.Test;
@@ -16,6 +19,7 @@ import org.keycloak.testsuite.Assert;
import org.keycloak.testsuite.pages.ConsentPage;
import org.keycloak.testsuite.util.*;
+import org.openqa.selenium.By;
import org.openqa.selenium.TimeoutException;
import java.util.Collections;
@@ -27,11 +31,14 @@ import java.util.stream.Collectors;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.keycloak.testsuite.admin.ApiUtil.createUserWithAdminClient;
+import static org.keycloak.testsuite.admin.ApiUtil.removeUserByUsername;
import static org.keycloak.testsuite.admin.ApiUtil.resetUserPassword;
import static org.keycloak.testsuite.broker.BrokerTestConstants.USER_EMAIL;
import static org.keycloak.testsuite.util.MailAssert.assertEmailAndGetUrl;
import org.jboss.arquillian.graphene.page.Page;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
import javax.ws.rs.core.Response;
@@ -49,6 +56,10 @@ public abstract class AbstractBrokerTest extends AbstractBaseBrokerTest {
protected IdentityProviderResource identityProviderResource;
+ @Drone
+ @SecondBrowser
+ protected WebDriver driver2;
+
@Before
public void beforeBrokerTest() {
log.debug("creating user for realm " + bc.providerRealmName());
@@ -181,15 +192,10 @@ public abstract class AbstractBrokerTest extends AbstractBaseBrokerTest {
MailServer.createEmailAccount(USER_EMAIL, "password");
try {
- //configure smpt server in the realm
- RealmRepresentation master = adminClient.realm(bc.consumerRealmName()).toRepresentation();
- master.setSmtpServer(suiteContext.getSmtpServer());
- adminClient.realm(bc.consumerRealmName()).update(master);
-
+ configureSMPTServer();
+
//create user on consumer's site who should be linked later
- UserRepresentation newUser = UserBuilder.create().username("consumer").email(USER_EMAIL).enabled(true).build();
- String userId = createUserWithAdminClient(adminClient.realm(bc.consumerRealmName()), newUser);
- resetUserPassword(adminClient.realm(bc.consumerRealmName()).users().get(userId), "password", false);
+ String linkedUserId = createUser("consumer");
//test
driver.navigate().to(getAccountUrl(bc.consumerRealmName()));
@@ -228,8 +234,74 @@ public abstract class AbstractBrokerTest extends AbstractBaseBrokerTest {
assertEquals(accountPage.buildUri().toASCIIString().replace("master", "consumer") + "/", driver.getCurrentUrl());
//test if the user has verified email
- assertTrue(adminClient.realm(bc.consumerRealmName()).users().get(userId).toRepresentation().isEmailVerified());
+ assertTrue(adminClient.realm(bc.consumerRealmName()).users().get(linkedUserId).toRepresentation().isEmailVerified());
} finally {
+ removeUserByUsername(adminClient.realm(bc.consumerRealmName()), "consumer");
+ // stop mail server
+ MailServer.stop();
+ }
+ }
+
+ @Test
+ public void testVerifyEmailInNewBrowserWithPreserveClient() {
+ //start mail server
+ MailServer.start();
+ MailServer.createEmailAccount(USER_EMAIL, "password");
+
+ try {
+ configureSMPTServer();
+
+ //create user on consumer's site who should be linked later
+ String linkedUserId = createUser("consumer");
+
+ driver.navigate().to(getLoginUrl(bc.consumerRealmName(), "broker-app"));
+
+ log.debug("Clicking social " + bc.getIDPAlias());
+ accountLoginPage.clickSocial(bc.getIDPAlias());
+
+ waitForPage(driver, "log in to", true);
+
+ Assert.assertTrue("Driver should be on the provider realm page right now",
+ driver.getCurrentUrl().contains("/auth/realms/" + bc.providerRealmName() + "/"));
+
+ log.debug("Logging in");
+ accountLoginPage.login(bc.getUserLogin(), bc.getUserPassword());
+
+ waitForPage(driver, "update account information", false);
+
+ Assert.assertTrue(updateAccountInformationPage.isCurrent());
+ Assert.assertTrue("We must be on correct realm right now",
+ driver.getCurrentUrl().contains("/auth/realms/" + bc.consumerRealmName() + "/"));
+
+ log.debug("Updating info on updateAccount page");
+ updateAccountInformationPage.updateAccountInformation("Firstname", "Lastname");
+
+ //link account by email
+ waitForPage(driver, "account already exists", false);
+ idpConfirmLinkPage.clickLinkAccount();
+
+ String url = assertEmailAndGetUrl(MailServerConfiguration.FROM, USER_EMAIL,
+ "Someone wants to link your ", false);
+
+ log.info("navigating to url from email in second browser: " + url);
+
+ // navigate to url in the second browser
+ driver2.navigate().to(url);
+
+ final WebElement proceedLink = driver2.findElement(By.linkText("» Click here to proceed"));
+ MatcherAssert.assertThat(proceedLink, Matchers.notNullValue());
+
+ // check if the initial client is preserved
+ String link = proceedLink.getAttribute("href");
+ MatcherAssert.assertThat(link, Matchers.containsString("client_id=broker-app"));
+ proceedLink.click();
+
+ assertThat(driver2.getPageSource(), Matchers.containsString("You successfully verified your email. Please go back to your original browser and continue there with the login."));
+
+ //test if the user has verified email
+ assertTrue(adminClient.realm(bc.consumerRealmName()).users().get(linkedUserId).toRepresentation().isEmailVerified());
+ } finally {
+ removeUserByUsername(adminClient.realm(bc.consumerRealmName()), "consumer");
// stop mail server
MailServer.stop();
}
@@ -431,4 +503,17 @@ public abstract class AbstractBrokerTest extends AbstractBaseBrokerTest {
String link = errorPage.getBackToApplicationLink();
Assert.assertTrue(link.endsWith("/auth/realms/consumer/account"));
}
+
+ private void configureSMPTServer() {
+ RealmRepresentation master = adminClient.realm(bc.consumerRealmName()).toRepresentation();
+ master.setSmtpServer(suiteContext.getSmtpServer());
+ adminClient.realm(bc.consumerRealmName()).update(master);
+ }
+
+ private String createUser(String username) {
+ UserRepresentation newUser = UserBuilder.create().username(username).email(USER_EMAIL).enabled(true).build();
+ String userId = createUserWithAdminClient(adminClient.realm(bc.consumerRealmName()), newUser);
+ resetUserPassword(adminClient.realm(bc.consumerRealmName()).users().get(userId), "password", false);
+ return userId;
+ }
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerConfiguration.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerConfiguration.java
index c6da797..5748179 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerConfiguration.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcOidcBrokerConfiguration.java
@@ -121,7 +121,20 @@ public class KcOidcBrokerConfiguration implements BrokerConfiguration {
@Override
public List<ClientRepresentation> createConsumerClients(SuiteContext suiteContext) {
- return null;
+ ClientRepresentation client = new ClientRepresentation();
+ client.setId("broker-app");
+ client.setClientId("broker-app");
+ client.setName("broker-app");
+ client.setSecret("broker-app-secret");
+ client.setEnabled(true);
+
+ client.setRedirectUris(Collections.singletonList(getAuthRoot(suiteContext) +
+ "/auth/*"));
+
+ client.setBaseUrl(getAuthRoot(suiteContext) +
+ "/auth/realms/" + REALM_CONS_NAME + "/app");
+
+ return Collections.singletonList(client);
}
@Override
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerConfiguration.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerConfiguration.java
index 904acd6..25877d4 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerConfiguration.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlBrokerConfiguration.java
@@ -166,6 +166,15 @@ public class KcSamlBrokerConfiguration implements BrokerConfiguration {
.addRedirectUri("http://localhost:8080/sales-post/*")
.attribute(SamlConfigAttributes.SAML_AUTHNSTATEMENT, SamlProtocol.ATTRIBUTE_TRUE_VALUE)
.attribute(SamlConfigAttributes.SAML_CLIENT_SIGNATURE_ATTRIBUTE, SamlProtocol.ATTRIBUTE_FALSE_VALUE)
+ .build(),
+ ClientBuilder.create()
+ .id("broker-app")
+ .clientId("broker-app")
+ .name("broker-app")
+ .secret("broker-app-secret")
+ .enabled(true)
+ .addRedirectUri(getAuthRoot(suiteContext) + "/auth/*")
+ .baseUrl(getAuthRoot(suiteContext) + "/auth/realms/" + REALM_CONS_NAME + "/app")
.build()
);
}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java
index 633494b..5f36170 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/forms/ResetPasswordTest.java
@@ -16,6 +16,8 @@
*/
package org.keycloak.testsuite.forms;
+import org.hamcrest.Matchers;
+import org.jboss.arquillian.drone.api.annotation.Drone;
import org.keycloak.admin.client.resource.ClientResource;
import org.keycloak.authentication.actiontoken.resetcred.ResetCredentialsActionToken;
import org.jboss.arquillian.graphene.page.Page;
@@ -40,9 +42,11 @@ import org.keycloak.testsuite.pages.LoginPasswordResetPage;
import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
import org.keycloak.testsuite.pages.VerifyEmailPage;
import org.keycloak.testsuite.updaters.ClientAttributeUpdater;
+import org.keycloak.testsuite.updaters.RealmAttributeUpdater;
import org.keycloak.testsuite.util.GreenMailRule;
import org.keycloak.testsuite.util.MailUtils;
import org.keycloak.testsuite.util.OAuthClient;
+import org.keycloak.testsuite.util.SecondBrowser;
import org.keycloak.testsuite.util.UserActionTokenBuilder;
import org.keycloak.testsuite.util.UserBuilder;
@@ -57,7 +61,11 @@ import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.*;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.*;
/**
@@ -68,6 +76,10 @@ public class ResetPasswordTest extends AbstractTestRealmKeycloakTest {
private String userId;
+ @Drone
+ @SecondBrowser
+ protected WebDriver driver2;
+
@Override
public void configureTestRealm(RealmRepresentation testRealm) {
}
@@ -182,7 +194,7 @@ public class ResetPasswordTest extends AbstractTestRealmKeycloakTest {
String changePasswordUrl = resetPassword("login-test");
events.clear();
- assertSecondPasswordResetFails(changePasswordUrl, null); // KC_RESTART doesn't exists, it was deleted after first successful reset-password flow was finished
+ assertSecondPasswordResetFails(changePasswordUrl, oauth.getClientId()); // KC_RESTART doesn't exists, it was deleted after first successful reset-password flow was finished
}
@Test
@@ -194,7 +206,7 @@ public class ResetPasswordTest extends AbstractTestRealmKeycloakTest {
driver.navigate().to(resetUri); // This is necessary to delete KC_RESTART cookie that is restricted to /auth/realms/test path
driver.manage().deleteAllCookies();
- assertSecondPasswordResetFails(changePasswordUrl, null);
+ assertSecondPasswordResetFails(changePasswordUrl, oauth.getClientId());
}
public void assertSecondPasswordResetFails(String changePasswordUrl, String clientId) {
@@ -204,7 +216,7 @@ public class ResetPasswordTest extends AbstractTestRealmKeycloakTest {
assertEquals("Action expired. Please continue with login now.", errorPage.getError());
events.expect(EventType.RESET_PASSWORD)
- .client("account")
+ .client(clientId)
.session((String) null)
.user(userId)
.error(Errors.EXPIRED_CODE)
@@ -1012,4 +1024,36 @@ public class ResetPasswordTest extends AbstractTestRealmKeycloakTest {
}
}
+ @Test
+ public void resetPasswordLinkNewBrowserSessionPreserveClient() throws IOException, MessagingException {
+ loginPage.open();
+ loginPage.resetPassword();
+
+ resetPasswordPage.assertCurrent();
+
+ resetPasswordPage.changePassword("login-test");
+
+ loginPage.assertCurrent();
+ assertEquals("You should receive an email shortly with further instructions.", loginPage.getSuccessMessage());
+
+ assertEquals(1, greenMail.getReceivedMessages().length);
+
+ MimeMessage message = greenMail.getReceivedMessages()[0];
+
+ String changePasswordUrl = MailUtils.getPasswordResetEmailLink(message);
+
+ driver2.navigate().to(changePasswordUrl.trim());
+
+ final WebElement newPassword = driver2.findElement(By.id("password-new"));
+ newPassword.sendKeys("resetPassword");
+ final WebElement confirmPassword = driver2.findElement(By.id("password-confirm"));
+ confirmPassword.sendKeys("resetPassword");
+ final WebElement submit = driver2.findElement(By.cssSelector("input[type=\"submit\"]"));
+ submit.click();
+
+ assertThat(driver2.getCurrentUrl(), Matchers.containsString("client_id=test-app"));
+
+ assertThat(driver2.getPageSource(), Matchers.containsString("Your account has been updated."));
+ }
+
}