diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java index 1354235465..ba56f18e28 100755 --- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java +++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractUsernameFormAuthenticator.java @@ -191,12 +191,7 @@ public abstract class AbstractUsernameFormAuthenticator extends AbstractFormAuth public boolean validatePassword(AuthenticationFlowContext context, UserModel user, MultivaluedMap inputData, boolean clearUser) { String password = inputData.getFirst(CredentialRepresentation.PASSWORD); if (password == null || password.isEmpty()) { - context.getEvent().user(user); - context.getEvent().error(Errors.INVALID_USER_CREDENTIALS); - Response challengeResponse = challenge(context, getDefaultChallengeMessage(context)); - context.forceChallenge(challengeResponse); - context.clearUser(); - return false; + return badPasswordHandler(context, user, clearUser,true); } if (isTemporarilyDisabledByBruteForce(context, user)) return false; @@ -204,17 +199,26 @@ public abstract class AbstractUsernameFormAuthenticator extends AbstractFormAuth if (password != null && !password.isEmpty() && context.getSession().userCredentialManager().isValid(context.getRealm(), user, UserCredentialModel.password(password))) { return true; } else { - context.getEvent().user(user); - context.getEvent().error(Errors.INVALID_USER_CREDENTIALS); - Response challengeResponse = challenge(context, getDefaultChallengeMessage(context)); - context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, challengeResponse); - if (clearUser) { - context.clearUser(); - } - return false; + return badPasswordHandler(context, user, clearUser,false); } } + // Set up AuthenticationFlowContext error. + private boolean badPasswordHandler(AuthenticationFlowContext context, UserModel user, boolean clearUser,boolean isEmptyPassword) { + context.getEvent().user(user); + context.getEvent().error(Errors.INVALID_USER_CREDENTIALS); + Response challengeResponse = challenge(context, getDefaultChallengeMessage(context)); + if(isEmptyPassword) { + context.forceChallenge(challengeResponse); + }else{ + context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, challengeResponse); + } + + if (clearUser) { + context.clearUser(); + } + return false; + } protected boolean isTemporarilyDisabledByBruteForce(AuthenticationFlowContext context, UserModel user) { if (context.getRealm().isBruteForceProtected()) { diff --git a/services/src/main/java/org/keycloak/forms/login/freemarker/model/AuthenticationContextBean.java b/services/src/main/java/org/keycloak/forms/login/freemarker/model/AuthenticationContextBean.java index b48e47f783..dde0aa7db4 100644 --- a/services/src/main/java/org/keycloak/forms/login/freemarker/model/AuthenticationContextBean.java +++ b/services/src/main/java/org/keycloak/forms/login/freemarker/model/AuthenticationContextBean.java @@ -49,7 +49,11 @@ public class AuthenticationContextBean { public boolean showUsername() { - return context != null && context.getUser() != null && context.getAuthenticationSession() != null; + return context != null && context.getUser() != null && context.getAuthenticationSession() != null && page!=LoginFormsPages.ERROR; + } + + public boolean showResetCredentials() { + return showUsername() && page == LoginFormsPages.LOGIN_RESET_PASSWORD; } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LanguageComboboxAwarePage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LanguageComboboxAwarePage.java index 2fd6955cd6..47e8d443b8 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LanguageComboboxAwarePage.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LanguageComboboxAwarePage.java @@ -42,7 +42,7 @@ public abstract class LanguageComboboxAwarePage extends AbstractPage { @FindBy(id = "try-another-way") private WebElement tryAnotherWayLink; - @FindBy(id = "attempted-username") + @FindBy(id = "kc-attempted-username") private WebElement attemptedUsernameLabel; // TODO: This won't be a link, but some kind of an icon once we do better design @@ -82,7 +82,7 @@ public abstract class LanguageComboboxAwarePage extends AbstractPage { public static void assertAttemptedUsernameAvailability(WebDriver driver, boolean expectedAvailability) { try { - driver.findElement(By.id("attempted-username")); + driver.findElement(By.id("kc-attempted-username")); Assert.assertTrue(expectedAvailability); } catch (NoSuchElementException nse) { Assert.assertFalse(expectedAvailability); diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginConfigTotpPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginConfigTotpPage.java index 9be3a96e59..4149e33e7a 100755 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginConfigTotpPage.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginConfigTotpPage.java @@ -16,6 +16,7 @@ */ package org.keycloak.testsuite.pages; +import org.openqa.selenium.By; import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; @@ -73,7 +74,12 @@ public class LoginConfigTotpPage extends AbstractPage { } public boolean isCurrent() { - return PageUtils.getPageTitle(driver).equals("Mobile Authenticator Setup"); + try { + driver.findElement(By.id("totp")); + return true; + } catch (Throwable t) { + return false; + } } public void open() { diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPasswordResetPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPasswordResetPage.java index 821a796334..b9fbf73936 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPasswordResetPage.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginPasswordResetPage.java @@ -40,6 +40,7 @@ public class LoginPasswordResetPage extends LanguageComboboxAwarePage { private WebElement backToLogin; public void changePassword(String username) { + usernameInput.clear(); usernameInput.sendKeys(username); submitButton.click(); diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginTotpPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginTotpPage.java index a7db0ed9b4..65f531ccc5 100755 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginTotpPage.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginTotpPage.java @@ -58,14 +58,12 @@ public class LoginTotpPage extends LanguageComboboxAwarePage { } public boolean isCurrent() { - if (driver.getTitle().startsWith("Log in to ")) { - try { - driver.findElement(By.id("otp")); - return true; - } catch (Throwable t) { - } + try { + driver.findElement(By.id("otp")); + return true; + } catch (Throwable t) { + return false; } - return false; } @Override diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/PasswordPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/PasswordPage.java index e6d6d5bd1d..475a8d3c55 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/PasswordPage.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/PasswordPage.java @@ -57,11 +57,6 @@ public class PasswordPage extends LanguageComboboxAwarePage { } public boolean isCurrent(String realm) { - // Check the title - if (!DroneUtils.getCurrentDriver().getTitle().equals("Log in to " + realm) && !DroneUtils.getCurrentDriver().getTitle().equals("Anmeldung bei " + realm)) { - return false; - } - // Check there is NO username field try { driver.findElement(By.id("username")); @@ -72,6 +67,7 @@ public class PasswordPage extends LanguageComboboxAwarePage { // Check there is password field try { + driver.findElement(By.id("kc-attempted-username")); driver.findElement(By.id("password")); } catch (NoSuchElementException nfe) { return false; diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/SelectAuthenticatorPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/SelectAuthenticatorPage.java index 656e4af24b..99ac60e2ac 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/SelectAuthenticatorPage.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/SelectAuthenticatorPage.java @@ -3,7 +3,6 @@ package org.keycloak.testsuite.pages; import java.util.List; import java.util.stream.Collectors; -import org.junit.Assert; import org.keycloak.testsuite.util.DroneUtils; import org.openqa.selenium.By; import org.openqa.selenium.NoSuchElementException; @@ -35,11 +34,7 @@ public class SelectAuthenticatorPage extends LanguageComboboxAwarePage { .stream() .filter(webElement -> webElement.getAttribute("selected") != null) .findFirst() - .orElseThrow(() -> { - - return new AssertionError("Selected login method not found"); - - }) + .orElseThrow(() -> new AssertionError("Selected login method not found")) .getText(); } diff --git a/themes/src/main/resources/theme/base/login/login-reset-password.ftl b/themes/src/main/resources/theme/base/login/login-reset-password.ftl index 720c274ebf..8a671c26fb 100755 --- a/themes/src/main/resources/theme/base/login/login-reset-password.ftl +++ b/themes/src/main/resources/theme/base/login/login-reset-password.ftl @@ -9,10 +9,13 @@
- + <#if auth?has_content && auth.showUsername()> + + <#else> + +
-
diff --git a/themes/src/main/resources/theme/base/login/messages/messages_en.properties b/themes/src/main/resources/theme/base/login/messages/messages_en.properties index b2022c55f6..da28d1f4f1 100755 --- a/themes/src/main/resources/theme/base/login/messages/messages_en.properties +++ b/themes/src/main/resources/theme/base/login/messages/messages_en.properties @@ -83,6 +83,8 @@ offlineAccessScopeConsentText=Offline Access samlRoleListScopeConsentText=My Roles rolesScopeConsentText=User roles +restartLoginTooltip=Restart login + loginTotpIntro=You need to set up a One Time Password generator to access this account loginTotpStep1=Install one of the following applications on your mobile loginTotpStep2=Open the application and scan the barcode diff --git a/themes/src/main/resources/theme/base/login/select-authenticator.ftl b/themes/src/main/resources/theme/base/login/select-authenticator.ftl index d3161dcaa0..80184aa54d 100644 --- a/themes/src/main/resources/theme/base/login/select-authenticator.ftl +++ b/themes/src/main/resources/theme/base/login/select-authenticator.ftl @@ -1,6 +1,6 @@ <#import "template.ftl" as layout> <@layout.registrationLayout displayInfo=true; section> - <#if section = "header"> + <#if section = "header" || section = "show-username">