Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@

package org.keycloak.authentication.authenticators.browser;

import org.jboss.logging.Logger;
import org.keycloak.authentication.AbstractFormAuthenticator;
import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.authentication.AuthenticationFlowError;
Expand All @@ -37,6 +36,7 @@

import jakarta.ws.rs.core.MultivaluedMap;
import jakarta.ws.rs.core.Response;
import org.keycloak.sessions.AuthenticationSessionModel;

import static org.keycloak.authentication.authenticators.util.AuthenticatorUtils.getDisabledByBruteForceEventError;
import static org.keycloak.services.validation.Validation.FIELD_PASSWORD;
Expand All @@ -48,10 +48,16 @@
*/
public abstract class AbstractUsernameFormAuthenticator extends AbstractFormAuthenticator {

private static final Logger logger = Logger.getLogger(AbstractUsernameFormAuthenticator.class);

public static final String REGISTRATION_FORM_ACTION = "registration_form";
public static final String ATTEMPTED_USERNAME = "ATTEMPTED_USERNAME";

/**
* An authentication session not to indicate that the username field should be hidden.
* This note is usually set together with {@link #ATTEMPTED_USERNAME} to indicated that the
* user can restart the flow by choosing a different username.
* It should be set by authenticators that happen before this authenticator in the flow so that the original intent
* is kept when this authenticator is executed on subsequent requests.
*/
public static final String USERNAME_HIDDEN = "USERNAME_HIDDEN";
public static final String SESSION_INVALID = "SESSION_INVALID";

// Flag is true if user was already set in the authContext before this authenticator was triggered. In this case we skip clearing of the user after unsuccessful password authentication
Expand All @@ -69,6 +75,14 @@ protected Response challenge(AuthenticationFlowContext context, String error) {
protected Response challenge(AuthenticationFlowContext context, String error, String field) {
LoginFormsProvider form = context.form()
.setExecution(context.getExecution().getId());

AuthenticationSessionModel authenticationSession = context.getAuthenticationSession();

if (Boolean.parseBoolean(authenticationSession.getAuthNote(USERNAME_HIDDEN))) {
// if username is hidden, shown errors in the password field instead
field = FIELD_PASSWORD;
}

if (error != null) {
if (field != null) {
form.addError(new FormMessage(field, error));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,9 @@ protected void createCommonAttributes(Theme theme, Locale locale, Properties mes
attributes.put("url", new UrlBean(realm, theme, baseUri, this.actionUri));
attributes.put("requiredActionUrl", new RequiredActionUrlFormatterMethod(realm, baseUri));
attributes.put("auth", new AuthenticationContextBean(context, page));
if (authenticationSession != null && Boolean.parseBoolean(authenticationSession.getAuthNote(AbstractUsernameFormAuthenticator.USERNAME_HIDDEN))) {
attributes.put(LoginFormsProvider.USERNAME_HIDDEN, Boolean.TRUE.toString());
}
setAttribute(Constants.EXECUTION, execution);

if (realm.isInternationalizationEnabled()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import org.keycloak.authentication.AuthenticationSelectionOption;
import org.keycloak.authentication.authenticators.browser.AbstractUsernameFormAuthenticator;
import org.keycloak.forms.login.LoginFormsPages;
import org.keycloak.sessions.AuthenticationSessionModel;

/**
* @author <a href="mailto:[email protected]">Marek Posolda</a>
Expand All @@ -49,7 +50,17 @@ public boolean showTryAnotherWayLink() {


public boolean showUsername() {
return context != null && context.getUser() != null && context.getAuthenticationSession() != null && page!=LoginFormsPages.ERROR;
if (context == null) {
return false;
}

AuthenticationSessionModel authenticationSession = context.getAuthenticationSession();

if (Boolean.parseBoolean(authenticationSession.getAuthNote(AbstractUsernameFormAuthenticator.USERNAME_HIDDEN))) {
return getAttemptedUsername() != null;
}

return context.getUser() != null && authenticationSession != null && page!=LoginFormsPages.ERROR;
}

public boolean showResetCredentials() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -430,18 +430,7 @@ private void attempted(AuthenticationFlowContext context, String username) {

if (username != null) {
authenticationSession.setAuthNote(AbstractUsernameFormAuthenticator.ATTEMPTED_USERNAME, username);
LoginFormsProvider form = context.form();

form.setAttributeMapper(attributes -> {
AuthenticationContextBean auth = (AuthenticationContextBean) attributes.get("auth");

if (auth != null) {
attributes.put("auth", new OrganizationAwareAuthenticationContextBean(auth, true, username));
attributes.put(LoginFormsProvider.USERNAME_HIDDEN, true);
}

return attributes;
});
authenticationSession.setAuthNote(AbstractUsernameFormAuthenticator.USERNAME_HIDDEN, Boolean.TRUE.toString());
}

context.attempted();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,11 @@ public class OrganizationAwareAuthenticationContextBean extends AuthenticationCo

private final AuthenticationContextBean delegate;
private final boolean showTryAnotherWayLink;
private final String username;

public OrganizationAwareAuthenticationContextBean(AuthenticationContextBean delegate, boolean showTryAnotherWayLink) {
this(delegate, showTryAnotherWayLink, null);
}

public OrganizationAwareAuthenticationContextBean(AuthenticationContextBean delegate, boolean showTryAnotherWayLink, String username) {
super(null, null);
this.delegate = delegate;
this.showTryAnotherWayLink = showTryAnotherWayLink;
this.username = username;
}

@Override
Expand All @@ -52,17 +46,14 @@ public boolean showTryAnotherWayLink() {
}

public boolean showUsername() {
return username != null || delegate.showUsername();
return delegate.showUsername();
}

public boolean showResetCredentials() {
return delegate.showResetCredentials();
}

public String getAttemptedUsername() {
if (username == null) {
return delegate.getAttemptedUsername();
}
return username;
return delegate.getAttemptedUsername();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ public static void assertAttemptedUsernameAvailability(WebDriver driver, boolean
try {
driver.findElement(By.id("kc-attempted-username"));
Assert.assertTrue(expectedAvailability);
// make sure the username field is not shown if the attempted username field is present
Assert.assertTrue(driver.findElements(By.id("username")).isEmpty());
} catch (NoSuchElementException nse) {
Assert.assertFalse(expectedAvailability);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,23 @@ public void testRestartLogin() {
Assert.assertFalse(loginPage.isPasswordInputPresent());
}

@Test
public void testAttemptedUsernameKeptAfterPasswordFailures() {
testRealm().organizations().get(createOrganization().getId());

openIdentityFirstLoginPage("[email protected]", false, null, false, false);

// check if the login page is shown
loginPage.assertAttemptedUsernameAvailability(true);
Assert.assertTrue(loginPage.isPasswordInputPresent());

for (int i = 0; i < 3; i++) {
loginPage.login("wrong-password");
loginPage.assertAttemptedUsernameAvailability(true);
Assert.assertTrue(loginPage.isPasswordInputPresent());
}
}

private void runOnServer(RunOnServer function) {
testingClient.server(bc.consumerRealmName()).run(function);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ public void testRealmLevelBrokersAvailableIfEmailDoesNotMatchOrganization() {

driver.navigate().refresh();

Assert.assertTrue(loginPage.isUsernameInputPresent());
loginPage.assertAttemptedUsernameAvailability(true);
Assert.assertTrue(loginPage.isPasswordInputPresent());
Assert.assertTrue(loginPage.isSocialButtonPresent(idp.getAlias()));
Assert.assertFalse(loginPage.isSocialButtonPresent(bc.getIDPAlias()));
Expand Down
Loading