keycloak-aplcache
Changes
services/src/main/java/org/keycloak/authentication/authenticators/AbstractFormAuthenticator.java 21(+18 -3)
services/src/main/java/org/keycloak/authentication/authenticators/LoginFormPasswordAuthenticator.java 13(+6 -7)
services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticator.java 15(+8 -7)
Details
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
index 570b9ca..c27fb1e 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
@@ -38,6 +38,7 @@ public class AuthenticationProcessor {
protected EventBuilder event;
protected HttpRequest request;
protected String flowId;
+ protected boolean userSessionCreated;
public static enum Status {
@@ -79,6 +80,14 @@ public class AuthenticationProcessor {
return session;
}
+ public UserSessionModel getUserSession() {
+ return userSession;
+ }
+
+ public boolean isUserSessionCreated() {
+ return userSessionCreated;
+ }
+
public AuthenticationProcessor setRealm(RealmModel realm) {
this.realm = realm;
return this;
@@ -339,6 +348,40 @@ public class AuthenticationProcessor {
throw new AuthException(Error.UNKNOWN_USER);
}
return authenticationComplete();
+ }
+
+ public Response authenticateOnly() throws AuthException {
+ event.event(EventType.LOGIN);
+ event.client(clientSession.getClient().getClientId())
+ .detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
+ .detail(Details.AUTH_METHOD, clientSession.getAuthMethod());
+ String authType = clientSession.getNote(Details.AUTH_TYPE);
+ if (authType != null) {
+ event.detail(Details.AUTH_TYPE, authType);
+ }
+ UserModel authUser = clientSession.getAuthenticatedUser();
+ validateUser(authUser);
+ Response challenge = processFlow(flowId);
+ if (challenge != null) return challenge;
+
+ String username = clientSession.getAuthenticatedUser().getUsername();
+ if (userSession == null) { // if no authenticator attached a usersession
+ userSession = session.sessions().createUserSession(realm, clientSession.getAuthenticatedUser(), username, connection.getRemoteAddr(), "form", false, null, null);
+ userSession.setState(UserSessionModel.State.LOGGING_IN);
+ userSessionCreated = true;
+ }
+ TokenManager.attachClientSession(userSession, clientSession);
+ event.user(userSession.getUser())
+ .detail(Details.USERNAME, username)
+ .session(userSession);
+
+ return AuthenticationManager.actionRequired(session, userSession, clientSession, connection, request, uriInfo, event);
+ }
+
+ public Response finishAuthentication() {
+ event.success();
+ RealmModel realm = clientSession.getRealm();
+ return AuthenticationManager.redirectAfterSuccessfulFlow(session, realm , userSession, clientSession, request, uriInfo, connection);
}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/AbstractFormAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/AbstractFormAuthenticator.java
index 0657e8a..20099f4 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/AbstractFormAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/AbstractFormAuthenticator.java
@@ -43,15 +43,28 @@ public class AbstractFormAuthenticator {
}
protected Response invalidUser(AuthenticatorContext context) {
- return loginForm(context).setError(Messages.INVALID_USER).createLogin();
+ return loginForm(context)
+ .setError(Messages.INVALID_USER)
+ .setClientSessionCode(new ClientSessionCode(context.getRealm(), context.getClientSession()).getCode())
+ .createLogin();
}
protected Response disabledUser(AuthenticatorContext context) {
- return loginForm(context).setError(Messages.ACCOUNT_DISABLED).createLogin();
+ return loginForm(context)
+ .setClientSessionCode(new ClientSessionCode(context.getRealm(), context.getClientSession()).getCode())
+ .setError(Messages.ACCOUNT_DISABLED).createLogin();
}
protected Response temporarilyDisabledUser(AuthenticatorContext context) {
- return loginForm(context).setError(Messages.ACCOUNT_TEMPORARILY_DISABLED).createLogin();
+ return loginForm(context)
+ .setClientSessionCode(new ClientSessionCode(context.getRealm(), context.getClientSession()).getCode())
+ .setError(Messages.ACCOUNT_TEMPORARILY_DISABLED).createLogin();
+ }
+
+ protected Response invalidCredentials(AuthenticatorContext context) {
+ return loginForm(context)
+ .setClientSessionCode(new ClientSessionCode(context.getRealm(), context.getClientSession()).getCode())
+ .setError(Messages.INVALID_USER).createLogin();
}
public boolean invalidUser(AuthenticatorContext context, UserModel user) {
@@ -62,6 +75,7 @@ public class AbstractFormAuthenticator {
return true;
}
if (!user.isEnabled()) {
+ context.getEvent().user(user);
context.getEvent().error(Errors.USER_DISABLED);
Response challengeResponse = disabledUser(context);
context.failureChallenge(AuthenticationProcessor.Error.USER_DISABLED, challengeResponse);
@@ -69,6 +83,7 @@ public class AbstractFormAuthenticator {
}
if (context.getRealm().isBruteForceProtected()) {
if (context.getProtector().isTemporarilyDisabled(context.getSession(), context.getRealm(), user.getUsername())) {
+ context.getEvent().user(user);
context.getEvent().error(Errors.USER_TEMPORARILY_DISABLED);
Response challengeResponse = temporarilyDisabledUser(context);
context.failureChallenge(AuthenticationProcessor.Error.USER_TEMPORARILY_DISABLED, challengeResponse);
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormPasswordAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormPasswordAuthenticator.java
index 98f532a..3995b3a 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormPasswordAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormPasswordAuthenticator.java
@@ -35,26 +35,25 @@ public class LoginFormPasswordAuthenticator extends LoginFormUsernameAuthenticat
validatePassword(context);
}
- protected Response badPassword(AuthenticatorContext context) {
- return loginForm(context).setError(Messages.INVALID_USER).createLogin();
- }
-
-
public void validatePassword(AuthenticatorContext context) {
MultivaluedMap<String, String> inputData = context.getHttpRequest().getFormParameters();
List<UserCredentialModel> credentials = new LinkedList<>();
String password = inputData.getFirst(CredentialRepresentation.PASSWORD);
if (password == null) {
+ if (context.getUser() != null) {
+ context.getEvent().user(context.getUser());
+ }
context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
- Response challengeResponse = badPassword(context);
+ Response challengeResponse = invalidCredentials(context);
context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
return;
}
credentials.add(UserCredentialModel.password(password));
boolean valid = context.getSession().users().validCredentials(context.getRealm(), context.getUser(), credentials);
if (!valid) {
+ context.getEvent().user(context.getUser());
context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
- Response challengeResponse = badPassword(context);
+ Response challengeResponse = invalidCredentials(context);
context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
return;
}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticator.java
index b5bf2d0..cf0d4fa 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticator.java
@@ -12,6 +12,7 @@ import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.ClientSessionCode;
+import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.LoginActionsService;
import javax.ws.rs.core.MultivaluedMap;
@@ -34,7 +35,7 @@ public class OTPFormAuthenticator extends AbstractFormAuthenticator implements A
@Override
public void authenticate(AuthenticatorContext context) {
if (!isActionUrl(context)) {
- Response challengeResponse = challenge(context);
+ Response challengeResponse = challenge(context, null);
context.challenge(challengeResponse);
return;
}
@@ -46,34 +47,34 @@ public class OTPFormAuthenticator extends AbstractFormAuthenticator implements A
List<UserCredentialModel> credentials = new LinkedList<>();
String password = inputData.getFirst(CredentialRepresentation.TOTP);
if (password == null) {
- Response challengeResponse = challenge(context);
+ Response challengeResponse = challenge(context, null);
context.challenge(challengeResponse);
return;
}
credentials.add(UserCredentialModel.totp(password));
boolean valid = context.getSession().users().validCredentials(context.getRealm(), context.getUser(), credentials);
if (!valid) {
- context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
- Response challengeResponse = challenge(context);
+ context.getEvent().user(context.getUser())
+ .error(Errors.INVALID_USER_CREDENTIALS);
+ Response challengeResponse = challenge(context, Messages.INVALID_TOTP);
context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
return;
}
context.success();
}
-
@Override
public boolean requiresUser() {
return true;
}
- protected Response challenge(AuthenticatorContext context) {
+ protected Response challenge(AuthenticatorContext context, String error) {
ClientSessionCode clientSessionCode = new ClientSessionCode(context.getRealm(), context.getClientSession());
URI action = AbstractFormAuthenticator.getActionUrl(context, clientSessionCode);
LoginFormsProvider forms = context.getSession().getProvider(LoginFormsProvider.class)
.setActionUri(action)
.setClientSessionCode(clientSessionCode.getCode());
-
+ if (error != null) forms.setError(error);
return forms.createLoginTotp();
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java
index 85e4067..a5baa07 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java
@@ -277,7 +277,21 @@ public class AuthorizationEndpoint {
.setUriInfo(uriInfo)
.setRequest(request);
- return processor.authenticate();
+ Response challenge = processor.authenticateOnly();
+
+ if (challenge != null && prompt != null && prompt.equals("none")) {
+ if (processor.isUserSessionCreated()) {
+ session.sessions().removeUserSession(realm, processor.getUserSession());
+ }
+ OIDCLoginProtocol oauth = new OIDCLoginProtocol(session, realm, uriInfo, headers, event);
+ return oauth.cancelLogin(clientSession);
+ }
+
+ if (challenge == null) {
+ return processor.finishAuthentication();
+ } else {
+ return challenge;
+ }
}
protected Response oldBrowserAuthentication(String accessCode) {
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index 3ada879..0cfca7a 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -425,6 +425,17 @@ public class AuthenticationManager {
public static Response nextActionAfterAuthentication(KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession,
ClientConnection clientConnection,
HttpRequest request, UriInfo uriInfo, EventBuilder event) {
+ Response requiredAction = actionRequired(session, userSession, clientSession, clientConnection, request, uriInfo, event);
+ if (requiredAction != null) return requiredAction;
+ event.success();
+ RealmModel realm = clientSession.getRealm();
+ return redirectAfterSuccessfulFlow(session, realm , userSession, clientSession, request, uriInfo, clientConnection);
+
+ }
+
+ public static Response actionRequired(KeycloakSession session, UserSessionModel userSession, ClientSessionModel clientSession,
+ ClientConnection clientConnection,
+ HttpRequest request, UriInfo uriInfo, EventBuilder event) {
RealmModel realm = clientSession.getRealm();
UserModel user = userSession.getUser();
isForcePasswordUpdateRequired(realm, user);
@@ -442,7 +453,7 @@ public class AuthenticationManager {
if (!requiredActions.isEmpty()) {
Iterator<String> i = user.getRequiredActions().iterator();
String action = i.next();
-
+
if (action.equals(UserModel.RequiredAction.VERIFY_EMAIL.name()) && Validation.isEmpty(user.getEmail())) {
if (i.hasNext())
action = i.next();
@@ -502,12 +513,12 @@ public class AuthenticationManager {
.createOAuthGrant(clientSession);
}
}
-
- event.success();
- return redirectAfterSuccessfulFlow(session, realm , userSession, clientSession, request, uriInfo, clientConnection);
+ return null;
}
-
+
+
+
private static void isForcePasswordUpdateRequired(RealmModel realm, UserModel user) {
int daysToExpirePassword = realm.getPasswordPolicy().getDaysToExpirePassword();
if(daysToExpirePassword != -1) {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/KerberosLdapTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/KerberosLdapTest.java
old mode 100644
new mode 100755
index 8086500..6db2d85
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/KerberosLdapTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/KerberosLdapTest.java
@@ -18,11 +18,13 @@ import org.keycloak.federation.ldap.kerberos.LDAPProviderKerberosConfig;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserFederationProviderModel;
+import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.rule.KerberosRule;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.WebRule;
+import org.keycloak.testsuite.utils.CredentialHelper;
/**
* Test of LDAPFederationProvider (Kerberos backed by LDAP)
@@ -41,6 +43,7 @@ public class KerberosLdapTest extends AbstractKerberosTest {
@Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ CredentialHelper.setRequiredCredential(CredentialRepresentation.KERBEROS, appRealm);
URL url = getClass().getResource("/kerberos-test/kerberos-app-keycloak.json");
keycloakRule.deployApplication("kerberos-portal", "/kerberos-portal", KerberosCredDelegServlet.class, url.getPath(), "user");
@@ -126,7 +129,7 @@ public class KerberosLdapTest extends AbstractKerberosTest {
.client("kerberos-app")
.user(keycloakRule.getUser("test", "jduke").getId())
.detail(Details.REDIRECT_URI, KERBEROS_APP_URL)
- .detail(Details.AUTH_METHOD, "spnego")
+ //.detail(Details.AUTH_METHOD, "spnego")
.detail(Details.USERNAME, "jduke")
.assertEvent();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java
index 644fff3..c373780 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTest.java
@@ -145,9 +145,9 @@ public class LoginTest {
loginPage.assertCurrent();
- Assert.assertEquals("Invalid username or password.", loginPage.getError());
+ Assert.assertEquals("Account is disabled, contact admin.", loginPage.getError());
- events.expectLogin().user(userId).session((String) null).error("invalid_user_credentials").detail(Details.USERNAME, "login-test").assertEvent();
+ events.expectLogin().user(userId).session((String) null).error("user_disabled").detail(Details.USERNAME, "login-test").assertEvent();
} finally {
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
@Override
@@ -225,7 +225,7 @@ public class LoginTest {
driver.navigate().to(oauth.getLoginFormUrl().toString() + "&prompt=none");
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
- events.expectLogin().user(userId).removeDetail(Details.USERNAME).detail(Details.AUTH_METHOD, "sso").assertEvent();
+ events.expectLogin().user(userId).removeDetail(Details.USERNAME).assertEvent();
}
@Test
@@ -359,7 +359,7 @@ public class LoginTest {
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
- events.expectLogin().user(userId).detail(Details.USERNAME, "login@test.com").assertEvent();
+ events.expectLogin().user(userId).assertEvent();
}
@Test
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java
index 45795da..e13fa0e 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/LoginTotpTest.java
@@ -110,9 +110,11 @@ public class LoginTotpTest {
loginTotpPage.assertCurrent();
loginTotpPage.login("123456");
+ loginTotpPage.assertCurrent();
+ Assert.assertEquals("Invalid authenticator code.", loginPage.getError());
- loginPage.assertCurrent();
- Assert.assertEquals("Invalid username or password.", loginPage.getError());
+ //loginPage.assertCurrent(); // Invalid authenticator code.
+ //Assert.assertEquals("Invalid username or password.", loginPage.getError());
events.expectLogin().error("invalid_user_credentials").session((String) null).assertEvent();
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/SSOTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/SSOTest.java
index 2f78c6d..f80118b 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/SSOTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/SSOTest.java
@@ -92,7 +92,7 @@ public class SSOTest {
assertTrue(profilePage.isCurrent());
- String sessionId2 = events.expectLogin().detail(Details.AUTH_METHOD, "sso").removeDetail(Details.USERNAME).client("test-app").assertEvent().getSessionId();
+ String sessionId2 = events.expectLogin().removeDetail(Details.USERNAME).client("test-app").assertEvent().getSessionId();
assertEquals(sessionId, sessionId2);
@@ -139,7 +139,7 @@ public class SSOTest {
oauth2.openLoginForm();
- events.expectLogin().session(login2.getSessionId()).detail(Details.AUTH_METHOD, "sso").removeDetail(Details.USERNAME).assertEvent();
+ events.expectLogin().session(login2.getSessionId()).removeDetail(Details.USERNAME).assertEvent();
Assert.assertEquals(RequestType.AUTH_RESPONSE, RequestType.valueOf(driver2.getTitle()));
Assert.assertNotNull(oauth2.getCurrentQuery().get(OAuth2Constants.CODE));