diff --git a/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java b/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java
index 2167996101..1733c06850 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java
@@ -18,14 +18,17 @@ public class DefaultAuthenticationFlows {
public static final String REGISTRATION_FLOW = "registration";
public static final String REGISTRATION_FORM_FLOW = "registration form";
public static final String BROWSER_FLOW = "browser";
+ public static final String DIRECT_GRANT_FLOW = "direct grant";
public static final String LOGIN_FORMS_FLOW = "forms";
public static void addFlows(RealmModel realm) {
if (realm.getFlowByAlias(BROWSER_FLOW) == null) browserFlow(realm);
+ if (realm.getFlowByAlias(DIRECT_GRANT_FLOW) == null) directGrantFlow(realm, false);
if (realm.getFlowByAlias(REGISTRATION_FLOW) == null) registrationFlow(realm);
}
public static void migrateFlows(RealmModel realm) {
browserFlow(realm, true);
+ directGrantFlow(realm, true);
if (realm.getFlowByAlias(REGISTRATION_FLOW) == null) registrationFlow(realm);
}
@@ -123,6 +126,55 @@ public class DefaultAuthenticationFlows {
return false;
}
+ public static void directGrantFlow(RealmModel realm, boolean migrate) {
+ AuthenticationFlowModel grant = new AuthenticationFlowModel();
+ grant.setAlias(DIRECT_GRANT_FLOW);
+ grant.setDescription("OpenID Connect Resource Owner Grant");
+ grant.setProviderId("basic-flow");
+ grant.setTopLevel(true);
+ grant.setBuiltIn(true);
+ grant = realm.addAuthenticationFlow(grant);
+
+ // username
+ AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
+ execution.setParentFlow(grant.getId());
+ execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
+ execution.setAuthenticator("direct-grant-validate-username");
+ execution.setPriority(10);
+ execution.setUserSetupAllowed(false);
+ execution.setAutheticatorFlow(false);
+ realm.addAuthenticatorExecution(execution);
+
+ // password
+ execution = new AuthenticationExecutionModel();
+ execution.setParentFlow(grant.getId());
+ execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
+ if (migrate && !hasCredentialType(realm, RequiredCredentialModel.PASSWORD.getType())) {
+ execution.setRequirement(AuthenticationExecutionModel.Requirement.DISABLED);
+ }
+ execution.setAuthenticator("direct-grant-validate-password");
+ execution.setPriority(20);
+ execution.setUserSetupAllowed(false);
+ execution.setAutheticatorFlow(false);
+ realm.addAuthenticatorExecution(execution);
+
+ // otp
+ execution = new AuthenticationExecutionModel();
+ execution.setParentFlow(grant.getId());
+ execution.setRequirement(AuthenticationExecutionModel.Requirement.OPTIONAL);
+ if (migrate && hasCredentialType(realm, RequiredCredentialModel.TOTP.getType())) {
+ execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
+ }
+ execution.setAuthenticator("direct-grant-validate-otp");
+ execution.setPriority(30);
+ execution.setUserSetupAllowed(false);
+ execution.setAutheticatorFlow(false);
+ realm.addAuthenticatorExecution(execution);
+
+
+
+ }
+
public static void browserFlow(RealmModel realm, boolean migrate) {
AuthenticationFlowModel browser = new AuthenticationFlowModel();
browser.setAlias(BROWSER_FLOW);
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
index 436762f245..1508746222 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
@@ -3,7 +3,7 @@ package org.keycloak.authentication;
import org.jboss.logging.Logger;
import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.ClientConnection;
-import org.keycloak.authentication.authenticators.AbstractFormAuthenticator;
+import org.keycloak.authentication.authenticators.browser.AbstractFormAuthenticator;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder;
@@ -561,9 +561,18 @@ public class AuthenticationProcessor {
validateUser(authUser);
AuthenticationFlow authenticationFlow = createFlowExecution(this.flowId, null);
Response challenge = authenticationFlow.processFlow();
- if (challenge != null) return challenge;
+ return challenge;
+ }
+ public Response attachSessionExecutionRequiredActions() {
+ attachSession();
+ return AuthenticationManager.actionRequired(session, userSession, clientSession, connection, request, uriInfo, event);
+ }
+
+ public void attachSession() {
String username = clientSession.getAuthenticatedUser().getUsername();
+ String attemptedUsername = clientSession.getNote(AbstractFormAuthenticator.ATTEMPTED_USERNAME);
+ if (attemptedUsername != null) username = attemptedUsername;
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);
@@ -573,8 +582,10 @@ public class AuthenticationProcessor {
event.user(userSession.getUser())
.detail(Details.USERNAME, username)
.session(userSession);
+ }
- return AuthenticationManager.actionRequired(session, userSession, clientSession, connection, request, uriInfo, event);
+ public void evaluateRequiredActionTriggers() {
+ AuthenticationManager.evaluateRequiredActionTriggers(session, userSession, clientSession, connection, request, uriInfo, event, realm, clientSession.getAuthenticatedUser());
}
public Response finishAuthentication() {
diff --git a/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java b/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java
index 058903c8d9..524d7a9f6d 100755
--- a/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java
+++ b/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java
@@ -105,6 +105,9 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
}
AuthenticatorFactory factory = (AuthenticatorFactory) processor.getSession().getKeycloakSessionFactory().getProviderFactory(Authenticator.class, model.getAuthenticator());
+ if (factory == null) {
+ throw new AuthenticationProcessor.AuthException("Could not find AuthenticatorFactory for: " + model.getAuthenticator(), AuthenticationProcessor.Error.INTERNAL_ERROR);
+ }
Authenticator authenticator = factory.create();
AuthenticationProcessor.logger.debugv("authenticator: {0}", factory.getId());
UserModel authUser = processor.getClientSession().getAuthenticatedUser();
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/AbstractFormAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractFormAuthenticator.java
similarity index 99%
rename from services/src/main/java/org/keycloak/authentication/authenticators/AbstractFormAuthenticator.java
rename to services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractFormAuthenticator.java
index 3fc70987f5..5e16237837 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/AbstractFormAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractFormAuthenticator.java
@@ -1,4 +1,4 @@
-package org.keycloak.authentication.authenticators;
+package org.keycloak.authentication.authenticators.browser;
import org.jboss.logging.Logger;
import org.keycloak.OAuth2Constants;
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/CookieAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/CookieAuthenticator.java
similarity index 95%
rename from services/src/main/java/org/keycloak/authentication/authenticators/CookieAuthenticator.java
rename to services/src/main/java/org/keycloak/authentication/authenticators/browser/CookieAuthenticator.java
index 0d5a1fb500..e814191bde 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/CookieAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/CookieAuthenticator.java
@@ -1,4 +1,4 @@
-package org.keycloak.authentication.authenticators;
+package org.keycloak.authentication.authenticators.browser;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorContext;
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/CookieAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/CookieAuthenticatorFactory.java
similarity index 94%
rename from services/src/main/java/org/keycloak/authentication/authenticators/CookieAuthenticatorFactory.java
rename to services/src/main/java/org/keycloak/authentication/authenticators/browser/CookieAuthenticatorFactory.java
index 5dcc99e0b0..da9209dacb 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/CookieAuthenticatorFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/CookieAuthenticatorFactory.java
@@ -1,10 +1,9 @@
-package org.keycloak.authentication.authenticators;
+package org.keycloak.authentication.authenticators.browser;
import org.keycloak.Config;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.models.AuthenticationExecutionModel;
-import org.keycloak.models.AuthenticatorConfigModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/OTPFormAuthenticator.java
similarity index 97%
rename from services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticator.java
rename to services/src/main/java/org/keycloak/authentication/authenticators/browser/OTPFormAuthenticator.java
index 2df69f1fe1..c6036865a3 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/OTPFormAuthenticator.java
@@ -1,11 +1,10 @@
-package org.keycloak.authentication.authenticators;
+package org.keycloak.authentication.authenticators.browser;
import org.keycloak.authentication.AuthenticationProcessor;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorContext;
import org.keycloak.events.Errors;
import org.keycloak.login.LoginFormsProvider;
-import org.keycloak.models.AuthenticatorConfigModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/OTPFormAuthenticatorFactory.java
similarity index 95%
rename from services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticatorFactory.java
rename to services/src/main/java/org/keycloak/authentication/authenticators/browser/OTPFormAuthenticatorFactory.java
index 2215d84a12..29b1c636cb 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticatorFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/OTPFormAuthenticatorFactory.java
@@ -1,10 +1,9 @@
-package org.keycloak.authentication.authenticators;
+package org.keycloak.authentication.authenticators.browser;
import org.keycloak.Config;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.models.AuthenticationExecutionModel;
-import org.keycloak.models.AuthenticatorConfigModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.UserCredentialModel;
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/SpnegoAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticator.java
similarity index 96%
rename from services/src/main/java/org/keycloak/authentication/authenticators/SpnegoAuthenticator.java
rename to services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticator.java
index 902deb3e0c..6caa1ab59d 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/SpnegoAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticator.java
@@ -1,4 +1,4 @@
-package org.keycloak.authentication.authenticators;
+package org.keycloak.authentication.authenticators.browser;
import org.jboss.logging.Logger;
import org.jboss.resteasy.spi.HttpRequest;
@@ -8,24 +8,19 @@ import org.keycloak.authentication.AuthenticatorContext;
import org.keycloak.constants.KerberosConstants;
import org.keycloak.events.Errors;
import org.keycloak.login.LoginFormsProvider;
-import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.CredentialValidationOutput;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
-import org.keycloak.models.UserSessionModel;
import org.keycloak.services.messages.Messages;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.net.URI;
-import java.util.HashMap;
import java.util.Map;
-import static org.keycloak.util.HtmlUtils.escapeAttribute;
-
/**
* @author Bill Burke
* @version $Revision: 1 $
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/SpnegoAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticatorFactory.java
similarity index 95%
rename from services/src/main/java/org/keycloak/authentication/authenticators/SpnegoAuthenticatorFactory.java
rename to services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticatorFactory.java
index 36b84e0b4e..462967580e 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/SpnegoAuthenticatorFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticatorFactory.java
@@ -1,10 +1,9 @@
-package org.keycloak.authentication.authenticators;
+package org.keycloak.authentication.authenticators.browser;
import org.keycloak.Config;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.models.AuthenticationExecutionModel;
-import org.keycloak.models.AuthenticatorConfigModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.UserCredentialModel;
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/UsernamePasswordForm.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordForm.java
similarity index 97%
rename from services/src/main/java/org/keycloak/authentication/authenticators/UsernamePasswordForm.java
rename to services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordForm.java
index 6632c82813..ec7163f00e 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/UsernamePasswordForm.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordForm.java
@@ -1,4 +1,4 @@
-package org.keycloak.authentication.authenticators;
+package org.keycloak.authentication.authenticators.browser;
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
import org.keycloak.authentication.AuthenticationProcessor;
@@ -6,7 +6,6 @@ import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorContext;
import org.keycloak.events.Errors;
import org.keycloak.login.LoginFormsProvider;
-import org.keycloak.models.AuthenticatorConfigModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/UsernamePasswordFormFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordFormFactory.java
similarity index 94%
rename from services/src/main/java/org/keycloak/authentication/authenticators/UsernamePasswordFormFactory.java
rename to services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordFormFactory.java
index ac2afee753..adc2815b48 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/UsernamePasswordFormFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordFormFactory.java
@@ -1,10 +1,9 @@
-package org.keycloak.authentication.authenticators;
+package org.keycloak.authentication.authenticators.browser;
import org.keycloak.Config;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.models.AuthenticationExecutionModel;
-import org.keycloak.models.AuthenticatorConfigModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.UserCredentialModel;
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/AbstractDirectGrantAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/AbstractDirectGrantAuthenticator.java
new file mode 100755
index 0000000000..965395fd05
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/AbstractDirectGrantAuthenticator.java
@@ -0,0 +1,59 @@
+package org.keycloak.authentication.authenticators.directgrant;
+
+import org.keycloak.Config;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.authentication.Authenticator;
+import org.keycloak.authentication.AuthenticatorContext;
+import org.keycloak.authentication.AuthenticatorFactory;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public abstract class AbstractDirectGrantAuthenticator implements Authenticator, AuthenticatorFactory {
+ public Response errorResponse(int status, String error, String errorDescription) {
+ Map e = new HashMap();
+ e.put(OAuth2Constants.ERROR, error);
+ if (errorDescription != null) {
+ e.put(OAuth2Constants.ERROR_DESCRIPTION, errorDescription);
+ }
+ return Response.status(status).entity(e).type(MediaType.APPLICATION_JSON_TYPE).build();
+ }
+
+ @Override
+ public void action(AuthenticatorContext context) {
+
+ }
+
+ @Override
+ public Authenticator create() {
+ return this;
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public Authenticator create(KeycloakSession session) {
+ return this;
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+
+ }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateOTP.java b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateOTP.java
new file mode 100755
index 0000000000..8a71f52a87
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateOTP.java
@@ -0,0 +1,125 @@
+package org.keycloak.authentication.authenticators.directgrant;
+
+import org.jboss.logging.Logger;
+import org.keycloak.authentication.AuthenticationProcessor;
+import org.keycloak.authentication.AuthenticatorContext;
+import org.keycloak.events.Errors;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.provider.ProviderConfigProperty;
+import org.keycloak.representations.idm.CredentialRepresentation;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class ValidateOTP extends AbstractDirectGrantAuthenticator {
+
+ private static final Logger logger = Logger.getLogger(ValidateOTP.class);
+ public static final String PROVIDER_ID = "direct-grant-validate-otp";
+
+ @Override
+ public void authenticate(AuthenticatorContext context) {
+ if (!isConfigured(context.getSession(), context.getRealm(), context.getUser())) {
+ if (context.getExecution().isOptional()) {
+ context.attempted();
+ } else if (context.getExecution().isRequired()) {
+ context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
+ Response challengeResponse = errorResponse(Response.Status.UNAUTHORIZED.getStatusCode(), "invalid_grant", "Invalid user credentials");
+ context.failure(AuthenticationProcessor.Error.INVALID_USER, challengeResponse);
+ }
+ return;
+ }
+ MultivaluedMap inputData = context.getHttpRequest().getDecodedFormParameters();
+ List credentials = new LinkedList<>();
+ String otp = inputData.getFirst(CredentialRepresentation.TOTP);
+ if (otp == null) {
+ if (context.getUser() != null) {
+ context.getEvent().user(context.getUser());
+ }
+ context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
+ Response challengeResponse = errorResponse(Response.Status.UNAUTHORIZED.getStatusCode(), "invalid_grant", "Invalid user credentials");
+ context.failure(AuthenticationProcessor.Error.INVALID_USER, challengeResponse);
+ return;
+ }
+ credentials.add(UserCredentialModel.totp(otp));
+ 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 = errorResponse(Response.Status.UNAUTHORIZED.getStatusCode(), "invalid_grant", "Invalid user credentials");
+ context.failure(AuthenticationProcessor.Error.INVALID_USER, challengeResponse);
+ return;
+ }
+
+ context.success();
+ }
+
+ @Override
+ public boolean requiresUser() {
+ return true;
+ }
+
+ @Override
+ public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
+ return true;
+ }
+
+ private boolean isConfigured(KeycloakSession session, RealmModel realm, UserModel user) {
+ return session.users().configuredForCredentialType(UserCredentialModel.TOTP, realm, user) && user.isTotp();
+ }
+
+ @Override
+ public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
+
+ }
+
+ @Override
+ public String getDisplayType() {
+ return "OTP";
+ }
+
+ @Override
+ public String getReferenceCategory() {
+ return null;
+ }
+
+ @Override
+ public boolean isConfigurable() {
+ return false;
+ }
+
+ public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
+ AuthenticationExecutionModel.Requirement.REQUIRED,
+ AuthenticationExecutionModel.Requirement.OPTIONAL,
+ AuthenticationExecutionModel.Requirement.DISABLED
+ };
+
+ @Override
+ public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
+ return REQUIREMENT_CHOICES;
+ }
+
+ @Override
+ public String getHelpText() {
+ return "Validates the one time password supplied as a 'totp' form parameter in direct grant request";
+ }
+
+ @Override
+ public List getConfigProperties() {
+ return new LinkedList<>();
+ }
+
+ @Override
+ public String getId() {
+ return PROVIDER_ID;
+ }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidatePassword.java b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidatePassword.java
new file mode 100755
index 0000000000..55ba2f8278
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidatePassword.java
@@ -0,0 +1,115 @@
+package org.keycloak.authentication.authenticators.directgrant;
+
+import org.jboss.logging.Logger;
+import org.keycloak.authentication.AuthenticationProcessor;
+import org.keycloak.authentication.AuthenticatorContext;
+import org.keycloak.authentication.authenticators.browser.AbstractFormAuthenticator;
+import org.keycloak.events.Details;
+import org.keycloak.events.Errors;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.provider.ProviderConfigProperty;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.services.managers.AuthenticationManager;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class ValidatePassword extends AbstractDirectGrantAuthenticator {
+
+ private static final Logger logger = Logger.getLogger(ValidatePassword.class);
+ public static final String PROVIDER_ID = "direct-grant-validate-password";
+
+ @Override
+ public void authenticate(AuthenticatorContext context) {
+ MultivaluedMap inputData = context.getHttpRequest().getDecodedFormParameters();
+ List 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 = errorResponse(Response.Status.UNAUTHORIZED.getStatusCode(), "invalid_grant", "Invalid user credentials");
+ context.failure(AuthenticationProcessor.Error.INVALID_USER, 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 = errorResponse(Response.Status.UNAUTHORIZED.getStatusCode(), "invalid_grant", "Invalid user credentials");
+ context.failure(AuthenticationProcessor.Error.INVALID_USER, challengeResponse);
+ return;
+ }
+
+ context.success();
+ }
+
+ @Override
+ public boolean requiresUser() {
+ return true;
+ }
+
+ @Override
+ public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
+ return true;
+ }
+
+ @Override
+ public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
+
+ }
+
+ @Override
+ public String getDisplayType() {
+ return "Password";
+ }
+
+ @Override
+ public String getReferenceCategory() {
+ return null;
+ }
+
+ @Override
+ public boolean isConfigurable() {
+ return false;
+ }
+
+ public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
+ AuthenticationExecutionModel.Requirement.REQUIRED,
+ AuthenticationExecutionModel.Requirement.DISABLED
+ };
+
+ @Override
+ public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
+ return REQUIREMENT_CHOICES;
+ }
+
+ @Override
+ public String getHelpText() {
+ return "Validates the password supplied as a 'password' form parameter in direct grant request";
+ }
+
+ @Override
+ public List getConfigProperties() {
+ return new LinkedList<>();
+ }
+
+ @Override
+ public String getId() {
+ return PROVIDER_ID;
+ }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateUsername.java b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateUsername.java
new file mode 100755
index 0000000000..330860b1a6
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateUsername.java
@@ -0,0 +1,144 @@
+package org.keycloak.authentication.authenticators.directgrant;
+
+import org.jboss.logging.Logger;
+import org.keycloak.Config;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.authentication.AuthenticationProcessor;
+import org.keycloak.authentication.Authenticator;
+import org.keycloak.authentication.AuthenticatorContext;
+import org.keycloak.authentication.AuthenticatorFactory;
+import org.keycloak.authentication.authenticators.browser.AbstractFormAuthenticator;
+import org.keycloak.events.Details;
+import org.keycloak.events.Errors;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.provider.ProviderConfigProperty;
+import org.keycloak.services.managers.AuthenticationManager;
+import org.keycloak.services.messages.Messages;
+
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class ValidateUsername extends AbstractDirectGrantAuthenticator {
+
+ private static final Logger logger = Logger.getLogger(ValidateUsername.class);
+ public static final String PROVIDER_ID = "direct-grant-validate-username";
+
+ @Override
+ public void authenticate(AuthenticatorContext context) {
+ MultivaluedMap inputData = context.getHttpRequest().getDecodedFormParameters();
+ String username = inputData.getFirst(AuthenticationManager.FORM_USERNAME);
+ if (username == null) {
+ context.getEvent().error(Errors.USER_NOT_FOUND);
+ Response challengeResponse = errorResponse(Response.Status.UNAUTHORIZED.getStatusCode(), "invalid_request", "Missing parameter: username");
+ context.failure(AuthenticationProcessor.Error.INVALID_USER, challengeResponse);
+ return;
+ }
+ context.getEvent().detail(Details.USERNAME, username);
+ context.getClientSession().setNote(AbstractFormAuthenticator.ATTEMPTED_USERNAME, username);
+
+ UserModel user = null;
+ try {
+ user = KeycloakModelUtils.findUserByNameOrEmail(context.getSession(), context.getRealm(), username);
+ } catch (ModelDuplicateException mde) {
+ logger.error(mde.getMessage(), mde);
+ Response challengeResponse = errorResponse(Response.Status.UNAUTHORIZED.getStatusCode(), "invalid_request", "Invalid user credentials");
+ context.failure(AuthenticationProcessor.Error.INVALID_USER, challengeResponse);
+ return;
+ }
+
+
+ if (user == null) {
+ context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
+ Response challengeResponse = errorResponse(Response.Status.UNAUTHORIZED.getStatusCode(), "invalid_grant", "Invalid user credentials");
+ context.failure(AuthenticationProcessor.Error.INVALID_USER, challengeResponse);
+ return;
+ }
+ if (!user.isEnabled()) {
+ context.getEvent().user(user);
+ context.getEvent().error(Errors.USER_DISABLED);
+ Response challengeResponse = errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "invalid_grant", "Account disabled");
+ context.failure(AuthenticationProcessor.Error.INVALID_USER, challengeResponse);
+ return;
+ }
+ 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 = errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "invalid_grant", "Account temporarily disabled");
+ context.failure(AuthenticationProcessor.Error.INVALID_USER, challengeResponse);
+ return;
+ }
+ }
+ context.setUser(user);
+ context.success();
+ }
+
+ @Override
+ public boolean requiresUser() {
+ return false;
+ }
+
+ @Override
+ public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
+ return true;
+ }
+
+ @Override
+ public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
+
+ }
+
+ @Override
+ public String getDisplayType() {
+ return "Username Validation";
+ }
+
+ @Override
+ public String getReferenceCategory() {
+ return null;
+ }
+
+ @Override
+ public boolean isConfigurable() {
+ return false;
+ }
+
+ public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
+ AuthenticationExecutionModel.Requirement.REQUIRED
+ };
+
+ @Override
+ public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
+ return REQUIREMENT_CHOICES;
+ }
+
+ @Override
+ public String getHelpText() {
+ return "Validates the username supplied as a 'username' form parameter in direct grant request";
+ }
+
+ @Override
+ public List getConfigProperties() {
+ return new LinkedList<>();
+ }
+
+ @Override
+ public String getId() {
+ return PROVIDER_ID;
+ }
+}
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 0919cf7050..bb4e0d38d4 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,6 +277,9 @@ public class AuthorizationEndpoint {
Response challenge = null;
try {
challenge = processor.authenticateOnly();
+ if (challenge == null) {
+ challenge = processor.attachSessionExecutionRequiredActions();
+ }
} catch (Exception e) {
return processor.handleBrowserException(e);
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
index eea4c1d702..3bff39ad86 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
@@ -5,11 +5,13 @@ import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.ClientConnection;
import org.keycloak.OAuth2Constants;
import org.keycloak.OAuthErrorException;
+import org.keycloak.authentication.AuthenticationProcessor;
import org.keycloak.constants.AdapterConstants;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType;
+import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
@@ -17,6 +19,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.UserSessionProvider;
+import org.keycloak.models.utils.DefaultAuthenticationFlows;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.TokenManager;
@@ -307,46 +310,38 @@ public class TokenEndpoint {
public Response buildResourceOwnerPasswordCredentialsGrant() {
event.detail(Details.AUTH_METHOD, "oauth_credentials").detail(Details.RESPONSE_TYPE, "token");
- String username = formParams.getFirst(AuthenticationManager.FORM_USERNAME);
- if (username == null) {
- event.error(Errors.USERNAME_MISSING);
- throw new ErrorResponseException("invalid_request", "Missing parameter: username", Response.Status.UNAUTHORIZED);
+ if (client.isConsentRequired()) {
+ throw new ErrorResponseException("invalid_client", "Client requires user consent", Response.Status.BAD_REQUEST);
}
- event.detail(Details.USERNAME, username);
-
- UserModel user = KeycloakModelUtils.findUserByNameOrEmail(session, realm, username);
- if (user != null) event.user(user);
-
- AuthenticationManager.AuthenticationStatus authenticationStatus = authManager.authenticateForm(session, clientConnection, realm, formParams);
- Map err;
-
- switch (authenticationStatus) {
- case SUCCESS:
- break;
- case ACCOUNT_TEMPORARILY_DISABLED:
- case ACTIONS_REQUIRED:
- event.error(Errors.USER_TEMPORARILY_DISABLED);
- throw new ErrorResponseException("invalid_grant", "Account temporarily disabled", Response.Status.BAD_REQUEST);
- case ACCOUNT_DISABLED:
- event.error(Errors.USER_DISABLED);
- throw new ErrorResponseException("invalid_grant", "Account disabled", Response.Status.BAD_REQUEST);
- default:
- event.error(Errors.INVALID_USER_CREDENTIALS);
- throw new ErrorResponseException("invalid_grant", "Invalid user credentials", Response.Status.UNAUTHORIZED);
- }
-
String scope = formParams.getFirst(OAuth2Constants.SCOPE);
UserSessionProvider sessions = session.sessions();
-
- UserSessionModel userSession = sessions.createUserSession(realm, user, username, clientConnection.getRemoteAddr(), "oauth_credentials", false, null, null);
- event.session(userSession);
-
ClientSessionModel clientSession = sessions.createClientSession(realm, client);
clientSession.setAuthMethod(OIDCLoginProtocol.LOGIN_PROTOCOL);
+ clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
clientSession.setNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()));
+ AuthenticationFlowModel flow = realm.getFlowByAlias(DefaultAuthenticationFlows.DIRECT_GRANT_FLOW);
+ String flowId = flow.getId();
+ AuthenticationProcessor processor = new AuthenticationProcessor();
+ processor.setClientSession(clientSession)
+ .setFlowId(flowId)
+ .setConnection(clientConnection)
+ .setEventBuilder(event)
+ .setProtector(authManager.getProtector())
+ .setRealm(realm)
+ .setSession(session)
+ .setUriInfo(uriInfo)
+ .setRequest(request);
+ Response challenge = processor.authenticateOnly();
+ if (challenge != null) return challenge;
+ processor.evaluateRequiredActionTriggers();
+ UserModel user = clientSession.getAuthenticatedUser();
+ if (user.getRequiredActions() != null && user.getRequiredActions().size() > 0) {
+ throw new ErrorResponseException("invalid_grant", "Account is not fully set up", Response.Status.BAD_REQUEST);
- TokenManager.attachClientSession(userSession, clientSession);
+ }
+ processor.attachSession();
+ UserSessionModel userSession = processor.getUserSession();
AccessTokenResponse res = tokenManager.responseBuilder(realm, client, event, session, userSession, clientSession)
.generateAccessToken(session, scope, client, user, userSession, clientSession)
@@ -354,6 +349,7 @@ public class TokenEndpoint {
.generateIDToken()
.build();
+
event.success();
return Cors.add(request, Response.ok(res, MediaType.APPLICATION_JSON_TYPE)).auth().allowedOrigins(client).allowedMethods("POST").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build();
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 eacb927c1f..c9ca6dee28 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -429,66 +429,7 @@ public class AuthenticationManager {
final UserModel user = userSession.getUser();
final ClientModel client = clientSession.getClient();
- RequiredActionContext context = new RequiredActionContext() {
- @Override
- public EventBuilder getEvent() {
- return event;
- }
-
- @Override
- public UserModel getUser() {
- return user;
- }
-
- @Override
- public RealmModel getRealm() {
- return realm;
- }
-
- @Override
- public ClientSessionModel getClientSession() {
- return clientSession;
- }
-
- @Override
- public UserSessionModel getUserSession() {
- return userSession;
- }
-
- @Override
- public ClientConnection getConnection() {
- return clientConnection;
- }
-
- @Override
- public UriInfo getUriInfo() {
- return uriInfo;
- }
-
- @Override
- public KeycloakSession getSession() {
- return session;
- }
-
- @Override
- public HttpRequest getHttpRequest() {
- return request;
- }
-
- @Override
- public String generateAccessCode(String action) {
- ClientSessionCode code = new ClientSessionCode(getRealm(), getClientSession());
- code.setAction(action);
- return code.getCode();
- }
- };
-
- // see if any required actions need triggering, i.e. an expired password
- for (RequiredActionProviderModel model : realm.getRequiredActionProviders()) {
- if (!model.isEnabled()) continue;
- RequiredActionProvider provider = session.getProvider(RequiredActionProvider.class, model.getProviderId());
- provider.evaluateTriggers(context);
- }
+ RequiredActionContext context = evaluateRequiredActionTriggers(session, userSession, clientSession, clientConnection, request, uriInfo, event, realm, user);
logger.debugv("processAccessCode: go to oauth page?: {0}", client.isConsentRequired());
@@ -554,6 +495,70 @@ public class AuthenticationManager {
}
+ public static RequiredActionContext evaluateRequiredActionTriggers(final KeycloakSession session, final UserSessionModel userSession, final ClientSessionModel clientSession, final ClientConnection clientConnection, final HttpRequest request, final UriInfo uriInfo, final EventBuilder event, final RealmModel realm, final UserModel user) {
+ RequiredActionContext context = new RequiredActionContext() {
+ @Override
+ public EventBuilder getEvent() {
+ return event;
+ }
+
+ @Override
+ public UserModel getUser() {
+ return user;
+ }
+
+ @Override
+ public RealmModel getRealm() {
+ return realm;
+ }
+
+ @Override
+ public ClientSessionModel getClientSession() {
+ return clientSession;
+ }
+
+ @Override
+ public UserSessionModel getUserSession() {
+ return userSession;
+ }
+
+ @Override
+ public ClientConnection getConnection() {
+ return clientConnection;
+ }
+
+ @Override
+ public UriInfo getUriInfo() {
+ return uriInfo;
+ }
+
+ @Override
+ public KeycloakSession getSession() {
+ return session;
+ }
+
+ @Override
+ public HttpRequest getHttpRequest() {
+ return request;
+ }
+
+ @Override
+ public String generateAccessCode(String action) {
+ ClientSessionCode code = new ClientSessionCode(getRealm(), getClientSession());
+ code.setAction(action);
+ return code.getCode();
+ }
+ };
+
+ // see if any required actions need triggering, i.e. an expired password
+ for (RequiredActionProviderModel model : realm.getRequiredActionProviders()) {
+ if (!model.isEnabled()) continue;
+ RequiredActionProvider provider = session.getProvider(RequiredActionProvider.class, model.getProviderId());
+ provider.evaluateTriggers(context);
+ }
+ return context;
+ }
+
protected static AuthResult verifyIdentityToken(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, boolean checkActive, String tokenString, HttpHeaders headers) {
try {
diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
index 55ddb9b50b..95f9be6590 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -23,14 +23,10 @@ package org.keycloak.services.resources;
import org.jboss.logging.Logger;
import org.jboss.resteasy.spi.HttpRequest;
-import org.jboss.resteasy.spi.InternalServerErrorException;
import org.keycloak.ClientConnection;
import org.keycloak.authentication.AuthenticationProcessor;
-import org.keycloak.authentication.AuthenticatorUtil;
import org.keycloak.authentication.RequiredActionContext;
import org.keycloak.authentication.RequiredActionProvider;
-import org.keycloak.authentication.authenticators.AbstractFormAuthenticator;
-import org.keycloak.authentication.authenticators.UsernamePasswordFormFactory;
import org.keycloak.email.EmailException;
import org.keycloak.email.EmailProvider;
import org.keycloak.events.Details;
@@ -55,8 +51,6 @@ import org.keycloak.models.utils.DefaultAuthenticationFlows;
import org.keycloak.models.utils.FormMessage;
import org.keycloak.models.utils.TimeBasedOTP;
import org.keycloak.protocol.LoginProtocol;
-import org.keycloak.protocol.oidc.OIDCLoginProtocol;
-import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.AuthenticationManager;
@@ -84,7 +78,6 @@ import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.Providers;
-import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;
diff --git a/services/src/main/java/org/keycloak/utils/CredentialHelper.java b/services/src/main/java/org/keycloak/utils/CredentialHelper.java
index 9b3af31618..dc43b370f2 100755
--- a/services/src/main/java/org/keycloak/utils/CredentialHelper.java
+++ b/services/src/main/java/org/keycloak/utils/CredentialHelper.java
@@ -5,16 +5,10 @@ import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.authentication.ConfigurableAuthenticatorFactory;
import org.keycloak.authentication.FormAction;
import org.keycloak.authentication.FormActionFactory;
-import org.keycloak.authentication.authenticators.OTPFormAuthenticatorFactory;
-import org.keycloak.authentication.authenticators.SpnegoAuthenticatorFactory;
-import org.keycloak.authentication.authenticators.UsernamePasswordFormFactory;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
-import org.keycloak.models.UserCredentialModel;
-import org.keycloak.models.utils.DefaultAuthenticationFlows;
-import org.keycloak.representations.idm.CredentialRepresentation;
/**
* used to set an execution a state based on type.
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory b/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory
index 409e84bdc1..14fa990bdd 100755
--- a/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory
+++ b/services/src/main/resources/META-INF/services/org.keycloak.authentication.AuthenticatorFactory
@@ -1,4 +1,7 @@
-org.keycloak.authentication.authenticators.CookieAuthenticatorFactory
-org.keycloak.authentication.authenticators.UsernamePasswordFormFactory
-org.keycloak.authentication.authenticators.OTPFormAuthenticatorFactory
-org.keycloak.authentication.authenticators.SpnegoAuthenticatorFactory
+org.keycloak.authentication.authenticators.browser.CookieAuthenticatorFactory
+org.keycloak.authentication.authenticators.browser.UsernamePasswordFormFactory
+org.keycloak.authentication.authenticators.browser.OTPFormAuthenticatorFactory
+org.keycloak.authentication.authenticators.browser.SpnegoAuthenticatorFactory
+org.keycloak.authentication.authenticators.directgrant.ValidateOTP
+org.keycloak.authentication.authenticators.directgrant.ValidatePassword
+org.keycloak.authentication.authenticators.directgrant.ValidateUsername
\ No newline at end of file
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java
index b307baa024..ee6c5c69f1 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java
@@ -18,7 +18,7 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.adapters.HttpClientBuilder;
-import org.keycloak.authentication.authenticators.SpnegoAuthenticator;
+import org.keycloak.authentication.authenticators.browser.SpnegoAuthenticator;
import org.keycloak.events.Details;
import org.keycloak.federation.kerberos.CommonKerberosConfig;
import org.keycloak.constants.KerberosConstants;
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ResourceOwnerPasswordCredentialsGrantTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ResourceOwnerPasswordCredentialsGrantTest.java
index 23d2e5e907..e5b44a9e0b 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ResourceOwnerPasswordCredentialsGrantTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/ResourceOwnerPasswordCredentialsGrantTest.java
@@ -86,7 +86,6 @@ public class ResourceOwnerPasswordCredentialsGrantTest {
.client("resource-owner")
.user(userId)
.session(accessToken.getSessionState())
- .detail(Details.AUTH_METHOD, "oauth_credentials")
.detail(Details.RESPONSE_TYPE, "token")
.detail(Details.TOKEN_ID, accessToken.getId())
.detail(Details.REFRESH_TOKEN_ID, refreshToken.getId())
@@ -123,7 +122,6 @@ public class ResourceOwnerPasswordCredentialsGrantTest {
events.expectLogin()
.client("resource-owner")
.session(accessToken.getSessionState())
- .detail(Details.AUTH_METHOD, "oauth_credentials")
.detail(Details.RESPONSE_TYPE, "token")
.detail(Details.TOKEN_ID, accessToken.getId())
.detail(Details.REFRESH_TOKEN_ID, refreshToken.getId())
@@ -178,7 +176,6 @@ public class ResourceOwnerPasswordCredentialsGrantTest {
events.expectLogin()
.client("resource-owner")
.session((String) null)
- .detail(Details.AUTH_METHOD, "oauth_credentials")
.detail(Details.RESPONSE_TYPE, "token")
.removeDetail(Details.CODE_ID)
.removeDetail(Details.REDIRECT_URI)
@@ -201,7 +198,6 @@ public class ResourceOwnerPasswordCredentialsGrantTest {
.client("resource-owner")
.user((String) null)
.session((String) null)
- .detail(Details.AUTH_METHOD, "oauth_credentials")
.detail(Details.RESPONSE_TYPE, "token")
.detail(Details.USERNAME, "invalid")
.removeDetail(Details.CODE_ID)