keycloak-memoizeit

document auth spi

8/9/2015 4:06:24 PM

Changes

services/src/main/java/org/keycloak/authentication/AuthenticatorContext.java 87(+0 -87)

Details

diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationFlowContext.java b/services/src/main/java/org/keycloak/authentication/AuthenticationFlowContext.java
new file mode 100755
index 0000000..6a683f3
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticationFlowContext.java
@@ -0,0 +1,177 @@
+package org.keycloak.authentication;
+
+import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.ClientConnection;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.models.AuthenticationExecutionModel;
+import org.keycloak.models.AuthenticatorConfigModel;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.services.managers.BruteForceProtector;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public interface AuthenticationFlowContext {
+    /**
+     * Current event builder being used
+     *
+     * @return
+     */
+    EventBuilder getEvent();
+
+    /**
+     * Create a refresh new EventBuilder to use within this context
+     *
+     * @return
+     */
+    EventBuilder newEvent();
+
+    /**
+     * The current execution in the flow
+     *
+     * @return
+     */
+    AuthenticationExecutionModel getExecution();
+
+    /**
+     * Current user attached to this flow.  It can return null if no uesr has been identified yet
+     *
+     * @return
+     */
+    UserModel getUser();
+
+    /**
+     * Attach a specific user to this flow.
+     *
+     * @param user
+     */
+    void setUser(UserModel user);
+
+    void attachUserSession(UserSessionModel userSession);
+
+    /**
+     * Current realm
+     *
+     * @return
+     */
+    RealmModel getRealm();
+
+    /**
+     * ClientSessionModel attached to this flow
+     *
+     * @return
+     */
+    ClientSessionModel getClientSession();
+
+    /**
+     * Information about the IP address from the connecting HTTP client.
+     *
+     * @return
+     */
+    ClientConnection getConnection();
+
+    /**
+     * UriInfo of the current request
+     *
+     * @return
+     */
+    UriInfo getUriInfo();
+
+    /**
+     * Current session
+     *
+     * @return
+     */
+    KeycloakSession getSession();
+
+    HttpRequest getHttpRequest();
+    BruteForceProtector getProtector();
+
+
+    /**
+     * Get any configuration associated with the current execution
+     *
+     * @return
+     */
+    AuthenticatorConfigModel getAuthenticatorConfig();
+
+    /**
+     * This could be an error message forwarded from brokering when the broker failed authentication
+     * and we want to continue authentication locally.  forwardedErrorMessage can then be displayed by
+     * whatever form is challenging.
+     */
+    String getForwardedErrorMessage();
+
+    /**
+     * Generates access code and updates clientsession timestamp
+     * Access codes must be included in form action callbacks as a query parameter.
+     *
+     * @return
+     */
+    String generateAccessCode();
+
+
+    AuthenticationExecutionModel.Requirement getCategoryRequirementFromCurrentFlow(String authenticatorCategory);
+
+    /**
+     * Mark the current execution as successful.  The flow will then continue
+     *
+     */
+    void success();
+
+    /**
+     * Aborts the current flow
+     *
+     * @param error
+     */
+    void failure(AuthenticationFlowError error);
+
+    /**
+     * Aborts the current flow.
+     *
+     * @param error
+     * @param response Response that will be sent back to HTTP client
+     */
+    void failure(AuthenticationFlowError error, Response response);
+
+    /**
+     * Sends a challenge response back to the HTTP client.  If the current execution requirement is optional, this response will not be
+     * sent.  If the current execution requirement is alternative, then this challenge will be sent if no other alternative
+     * execution was successful.
+     *
+     * @param challenge
+     */
+    void challenge(Response challenge);
+
+    /**
+     * Sends the challenge back to the HTTP client irregardless of the current executionr equirement
+     *
+     * @param challenge
+     */
+    void forceChallenge(Response challenge);
+
+    /**
+     * Same behavior as challenge(), but the error count in brute force attack detection will be incremented.
+     * For example, if a user enters in a bad password, the user is directed to try again, but Keycloak will keep track
+     * of how many failures have happened.
+     *
+     * @param error
+     * @param challenge
+     */
+    void failureChallenge(AuthenticationFlowError error, Response challenge);
+
+    /**
+     * There was no failure or challenge.  The authenticator was attempted, but not fulfilled.  If the current execution
+     * requirement is alternative or optional, then this status is ignored by the flow.
+     *
+     */
+    void attempted();
+}
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationFlowError.java b/services/src/main/java/org/keycloak/authentication/AuthenticationFlowError.java
new file mode 100755
index 0000000..ec60973
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticationFlowError.java
@@ -0,0 +1,20 @@
+package org.keycloak.authentication;
+
+/**
+ * Set of error codes that can be thrown by an Authenticator, FormAuthenticator, or FormAction
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public enum AuthenticationFlowError {
+    EXPIRED_CODE,
+    INVALID_CLIENT_SESSION,
+    INVALID_USER,
+    INVALID_CREDENTIALS,
+    CREDENTIAL_SETUP_REQUIRED,
+    USER_DISABLED,
+    USER_CONFLICT,
+    USER_TEMPORARILY_DISABLED,
+    INTERNAL_ERROR,
+    UNKNOWN_USER
+}
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationFlowException.java b/services/src/main/java/org/keycloak/authentication/AuthenticationFlowException.java
new file mode 100755
index 0000000..db4e4dd
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticationFlowException.java
@@ -0,0 +1,39 @@
+package org.keycloak.authentication;
+
+/**
+ * Throw this exception from an Authenticator, FormAuthenticator, or FormAction if you want to completely abort the flow.
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class AuthenticationFlowException extends RuntimeException {
+    private AuthenticationFlowError error;
+
+    public AuthenticationFlowException(AuthenticationFlowError error) {
+        this.error = error;
+    }
+
+    public AuthenticationFlowException(String message, AuthenticationFlowError error) {
+        super(message);
+        this.error = error;
+    }
+
+    public AuthenticationFlowException(String message, Throwable cause, AuthenticationFlowError error) {
+        super(message, cause);
+        this.error = error;
+    }
+
+    public AuthenticationFlowException(Throwable cause, AuthenticationFlowError error) {
+        super(cause);
+        this.error = error;
+    }
+
+    public AuthenticationFlowException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, AuthenticationFlowError error) {
+        super(message, cause, enableSuppression, writableStackTrace);
+        this.error = error;
+    }
+
+    public AuthenticationFlowError getError() {
+        return error;
+    }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
index f3daf78..3c16552 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
@@ -63,19 +63,6 @@ public class AuthenticationProcessor {
 
     }
 
-    public static enum Error {
-        EXPIRED_CODE,
-        INVALID_CLIENT_SESSION,
-        INVALID_USER,
-        INVALID_CREDENTIALS,
-        CREDENTIAL_SETUP_REQUIRED,
-        USER_DISABLED,
-        USER_CONFLICT,
-        USER_TEMPORARILY_DISABLED,
-        INTERNAL_ERROR,
-        UNKNOWN_USER
-    }
-
     public RealmModel getRealm() {
         return realm;
     }
@@ -176,19 +163,19 @@ public class AuthenticationProcessor {
     public void setAutheticatedUser(UserModel user) {
         UserModel previousUser = clientSession.getAuthenticatedUser();
         if (previousUser != null && !user.getId().equals(previousUser.getId()))
-            throw new AuthException(Error.USER_CONFLICT);
+            throw new AuthenticationFlowException(AuthenticationFlowError.USER_CONFLICT);
         validateUser(user);
         getClientSession().setAuthenticatedUser(user);
     }
 
 
-    private class Result implements AuthenticatorContext {
+    public class Result implements AuthenticationFlowContext {
         AuthenticatorConfigModel authenticatorConfig;
         AuthenticationExecutionModel execution;
         Authenticator authenticator;
         Status status;
         Response challenge;
-        Error error;
+        AuthenticationFlowError error;
         List<AuthenticationExecutionModel> currentExecutions;
 
         private Result(AuthenticationExecutionModel execution, Authenticator authenticator, List<AuthenticationExecutionModel> currentExecutions) {
@@ -221,11 +208,6 @@ public class AuthenticationProcessor {
         }
 
         @Override
-        public void setExecution(AuthenticationExecutionModel execution) {
-            this.execution = execution;
-        }
-
-        @Override
         public AuthenticatorConfigModel getAuthenticatorConfig() {
             if (execution.getAuthenticatorConfig() == null) return null;
             if (authenticatorConfig != null) return authenticatorConfig;
@@ -233,17 +215,10 @@ public class AuthenticationProcessor {
             return authenticatorConfig;
         }
 
-        @Override
         public Authenticator getAuthenticator() {
             return authenticator;
         }
 
-        @Override
-        public void setAuthenticator(Authenticator authenticator) {
-            this.authenticator = authenticator;
-        }
-
-        @Override
         public Status getStatus() {
             return status;
         }
@@ -254,7 +229,7 @@ public class AuthenticationProcessor {
         }
 
         @Override
-        public void failure(Error error) {
+        public void failure(AuthenticationFlowError error) {
             status = Status.FAILED;
             this.error = error;
 
@@ -275,7 +250,7 @@ public class AuthenticationProcessor {
         }
 
         @Override
-        public void failureChallenge(Error error, Response challenge) {
+        public void failureChallenge(AuthenticationFlowError error, Response challenge) {
             this.error = error;
             this.status = Status.FAILURE_CHALLENGE;
             this.challenge = challenge;
@@ -283,7 +258,7 @@ public class AuthenticationProcessor {
         }
 
         @Override
-        public void failure(Error error, Response challenge) {
+        public void failure(AuthenticationFlowError error, Response challenge) {
             this.error = error;
             this.status = Status.FAILED;
             this.challenge = challenge;
@@ -362,45 +337,11 @@ public class AuthenticationProcessor {
         }
 
 
-        @Override
         public Response getChallenge() {
             return challenge;
         }
 
-        @Override
-        public Error getError() {
-            return error;
-        }
-    }
-
-    public static class AuthException extends RuntimeException {
-        private Error error;
-
-        public AuthException(Error error) {
-            this.error = error;
-        }
-
-        public AuthException(String message, Error error) {
-            super(message);
-            this.error = error;
-        }
-
-        public AuthException(String message, Throwable cause, Error error) {
-            super(message, cause);
-            this.error = error;
-        }
-
-        public AuthException(Throwable cause, Error error) {
-            super(cause);
-            this.error = error;
-        }
-
-        public AuthException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Error error) {
-            super(message, cause, enableSuppression, writableStackTrace);
-            this.error = error;
-        }
-
-        public Error getError() {
+        public AuthenticationFlowError getError() {
             return error;
         }
     }
@@ -425,24 +366,24 @@ public class AuthenticationProcessor {
     }
 
     public Response handleBrowserException(Exception failure) {
-        if (failure instanceof AuthException) {
-            AuthException e = (AuthException) failure;
+        if (failure instanceof AuthenticationFlowException) {
+            AuthenticationFlowException e = (AuthenticationFlowException) failure;
             logger.error("failed authentication: " + e.getError().toString(), e);
-            if (e.getError() == AuthenticationProcessor.Error.INVALID_USER) {
+            if (e.getError() == AuthenticationFlowError.INVALID_USER) {
                 event.error(Errors.USER_NOT_FOUND);
                 return ErrorPage.error(session, Messages.INVALID_USER);
-            } else if (e.getError() == AuthenticationProcessor.Error.USER_DISABLED) {
+            } else if (e.getError() == AuthenticationFlowError.USER_DISABLED) {
                 event.error(Errors.USER_DISABLED);
                 return ErrorPage.error(session, Messages.ACCOUNT_DISABLED);
-            } else if (e.getError() == AuthenticationProcessor.Error.USER_TEMPORARILY_DISABLED) {
+            } else if (e.getError() == AuthenticationFlowError.USER_TEMPORARILY_DISABLED) {
                 event.error(Errors.USER_TEMPORARILY_DISABLED);
                 return ErrorPage.error(session, Messages.ACCOUNT_TEMPORARILY_DISABLED);
 
-            } else if (e.getError() == Error.INVALID_CLIENT_SESSION) {
+            } else if (e.getError() == AuthenticationFlowError.INVALID_CLIENT_SESSION) {
                 event.error(Errors.INVALID_CODE);
                 return ErrorPage.error(session, Messages.INVALID_CODE);
 
-            } else if (e.getError() == Error.EXPIRED_CODE) {
+            } else if (e.getError() == AuthenticationFlowError.EXPIRED_CODE) {
                 event.error(Errors.EXPIRED_CODE);
                 return ErrorPage.error(session, Messages.EXPIRED_CODE);
 
@@ -463,7 +404,7 @@ public class AuthenticationProcessor {
         AuthenticationFlowModel flow = realm.getAuthenticationFlowById(flowId);
         if (flow == null) {
             logger.error("Unknown flow to execute with");
-            throw new AuthException(Error.INTERNAL_ERROR);
+            throw new AuthenticationFlowException(AuthenticationFlowError.INTERNAL_ERROR);
         }
         if (flow.getProviderId() == null || flow.getProviderId().equals(AuthenticationFlow.BASIC_FLOW)) {
             DefaultAuthenticationFlow flowExecution = new DefaultAuthenticationFlow(this, flow);
@@ -473,10 +414,10 @@ public class AuthenticationProcessor {
             FormAuthenticationFlow flowExecution = new FormAuthenticationFlow(this, execution);
             return flowExecution;
         }
-        throw new AuthException("Unknown flow provider type", Error.INTERNAL_ERROR);
+        throw new AuthenticationFlowException("Unknown flow provider type", AuthenticationFlowError.INTERNAL_ERROR);
     }
 
-    public Response authenticate() throws AuthException {
+    public Response authenticate() throws AuthenticationFlowException {
         checkClientSession();
         logger.debug("AUTHENTICATE");
         event.client(clientSession.getClient().getClientId())
@@ -492,7 +433,7 @@ public class AuthenticationProcessor {
         Response challenge = authenticationFlow.processFlow();
         if (challenge != null) return challenge;
         if (clientSession.getAuthenticatedUser() == null) {
-            throw new AuthException(Error.UNKNOWN_USER);
+            throw new AuthenticationFlowException(AuthenticationFlowError.UNKNOWN_USER);
         }
         return authenticationComplete();
     }
@@ -533,7 +474,7 @@ public class AuthenticationProcessor {
         Response challenge = authenticationFlow.processAction(execution);
         if (challenge != null) return challenge;
         if (clientSession.getAuthenticatedUser() == null) {
-            throw new AuthException(Error.UNKNOWN_USER);
+            throw new AuthenticationFlowException(AuthenticationFlowError.UNKNOWN_USER);
         }
         return authenticationComplete();
     }
@@ -541,15 +482,15 @@ public class AuthenticationProcessor {
     public void checkClientSession() {
         ClientSessionCode code = new ClientSessionCode(realm, clientSession);
         if (!code.isValidAction(ClientSessionModel.Action.AUTHENTICATE.name())) {
-            throw new AuthException(Error.INVALID_CLIENT_SESSION);
+            throw new AuthenticationFlowException(AuthenticationFlowError.INVALID_CLIENT_SESSION);
         }
         if (!code.isActionActive(ClientSessionModel.Action.AUTHENTICATE.name())) {
-            throw new AuthException(Error.EXPIRED_CODE);
+            throw new AuthenticationFlowException(AuthenticationFlowError.EXPIRED_CODE);
         }
         clientSession.setTimestamp(Time.currentTime());
     }
 
-    public Response authenticateOnly() throws AuthException {
+    public Response authenticateOnly() throws AuthenticationFlowException {
         checkClientSession();
         event.client(clientSession.getClient().getClientId())
                 .detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
@@ -599,10 +540,10 @@ public class AuthenticationProcessor {
 
     public void validateUser(UserModel authenticatedUser) {
         if (authenticatedUser == null) return;
-        if (!authenticatedUser.isEnabled()) throw new AuthException(Error.USER_DISABLED);
+        if (!authenticatedUser.isEnabled()) throw new AuthenticationFlowException(AuthenticationFlowError.USER_DISABLED);
         if (realm.isBruteForceProtected()) {
             if (protector.isTemporarilyDisabled(session, realm, authenticatedUser.getUsername())) {
-                throw new AuthException(Error.USER_TEMPORARILY_DISABLED);
+                throw new AuthenticationFlowException(AuthenticationFlowError.USER_TEMPORARILY_DISABLED);
             }
         }
     }
@@ -627,7 +568,7 @@ public class AuthenticationProcessor {
 
     }
 
-    public AuthenticatorContext createAuthenticatorContext(AuthenticationExecutionModel model, Authenticator authenticator, List<AuthenticationExecutionModel> executions) {
+    public AuthenticationProcessor.Result createAuthenticatorContext(AuthenticationExecutionModel model, Authenticator authenticator, List<AuthenticationExecutionModel> executions) {
         return new Result(model, authenticator, executions);
     }
 
diff --git a/services/src/main/java/org/keycloak/authentication/Authenticator.java b/services/src/main/java/org/keycloak/authentication/Authenticator.java
index 932e4ad..72c89db 100755
--- a/services/src/main/java/org/keycloak/authentication/Authenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/Authenticator.java
@@ -15,11 +15,33 @@ import org.keycloak.provider.Provider;
 public interface Authenticator extends Provider {
 
     /**
-     * Initial call for the authenticator.  If this is a form, a challenge with a Response rendering the form is usually sent
+     * Initial call for the authenticator.  This method should check the current HTTP request to determine if the request
+     * satifies the Authenticator's requirements.  If it doesn't, it should send back a challenge response by calling
+     * the AuthenticationFlowContext.challenge(Response).  If this challenge is a authentication, the action URL
+     * of the form must point to
+     *
+     * /realms/{realm}/login-actions/authenticate?code={session-code}&execution={executionId}
+     *
+     * or
+     *
+     * /realms/{realm}/login-actions/registration?code={session-code}&execution={executionId}
+     *
+     * {session-code} pertains to the code generated from AuthenticationFlowContext.generateAccessCode().  The {executionId}
+     * pertains to the AuthenticationExecutionModel.getId() value obtained from AuthenticationFlowContext.getExecution().
+     *
+     * The action URL will invoke the action() method described below.
      *
      * @param context
      */
-    void authenticate(AuthenticatorContext context);
+    void authenticate(AuthenticationFlowContext context);
+
+    /**
+     * Called from a form action invocation.
+     *
+     * @param context
+     */
+    void action(AuthenticationFlowContext context);
+
 
     /**
      * Does this authenticator require that the user has already been identified?  That AuthenticatorContext.getUser() is not null?
@@ -44,12 +66,6 @@ public interface Authenticator extends Provider {
      */
     void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user);
 
-    /**
-     * Usually implements a form action.
-     *
-     * @param context
-     */
-    void action(AuthenticatorContext context);
 
 
 }
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/AuthenticatorFactory.java
index 7e5e691..ccc854b 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticatorFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticatorFactory.java
@@ -6,7 +6,7 @@ import org.keycloak.provider.ConfiguredProvider;
 import org.keycloak.provider.ProviderFactory;
 
 /**
- * Factory for creating Authenticator instances
+ * Factory for creating Authenticator instances.  This is a singleton and created when Keycloak boots.
  *
  * You must specify a file
  * META-INF/services/org.keycloak.authentication.AuthenticatorFactory in the jar that this class is contained in
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractFormAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractFormAuthenticator.java
index 9ac6f07..46e71d4 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractFormAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractFormAuthenticator.java
@@ -2,9 +2,9 @@ package org.keycloak.authentication.authenticators.browser;
 
 import org.jboss.logging.Logger;
 import org.keycloak.OAuth2Constants;
-import org.keycloak.authentication.AuthenticationProcessor;
+import org.keycloak.authentication.AuthenticationFlowError;
+import org.keycloak.authentication.AuthenticationFlowContext;
 import org.keycloak.authentication.Authenticator;
-import org.keycloak.authentication.AuthenticatorContext;
 import org.keycloak.events.Details;
 import org.keycloak.events.Errors;
 import org.keycloak.login.LoginFormsProvider;
@@ -36,7 +36,7 @@ public abstract class AbstractFormAuthenticator implements Authenticator {
     public static final String ATTEMPTED_USERNAME = "ATTEMPTED_USERNAME";
 
     @Override
-    public void action(AuthenticatorContext context) {
+    public void action(AuthenticationFlowContext context) {
 
     }
 
@@ -45,7 +45,7 @@ public abstract class AbstractFormAuthenticator implements Authenticator {
 
     }
 
-    protected LoginFormsProvider loginForm(AuthenticatorContext context) {
+    protected LoginFormsProvider loginForm(AuthenticationFlowContext context) {
         String accessCode = context.generateAccessCode();
         URI action = getActionUrl(context, accessCode);
         LoginFormsProvider provider = context.getSession().getProvider(LoginFormsProvider.class)
@@ -58,35 +58,35 @@ public abstract class AbstractFormAuthenticator implements Authenticator {
         return provider;
     }
 
-    public URI getActionUrl(AuthenticatorContext context, String code) {
+    public URI getActionUrl(AuthenticationFlowContext context, String code) {
         return LoginActionsService.authenticationFormProcessor(context.getUriInfo())
                 .queryParam(OAuth2Constants.CODE, code)
                 .queryParam(EXECUTION, context.getExecution().getId())
                     .build(context.getRealm().getName());
     }
 
-    protected Response invalidUser(AuthenticatorContext context) {
+    protected Response invalidUser(AuthenticationFlowContext context) {
         return loginForm(context)
                 .setError(Messages.INVALID_USER)
                 .createLogin();
     }
 
-    protected Response disabledUser(AuthenticatorContext context) {
+    protected Response disabledUser(AuthenticationFlowContext context) {
         return loginForm(context)
                 .setError(Messages.ACCOUNT_DISABLED).createLogin();
     }
 
-    protected Response temporarilyDisabledUser(AuthenticatorContext context) {
+    protected Response temporarilyDisabledUser(AuthenticationFlowContext context) {
         return loginForm(context)
                 .setError(Messages.ACCOUNT_TEMPORARILY_DISABLED).createLogin();
     }
 
-    protected Response invalidCredentials(AuthenticatorContext context) {
+    protected Response invalidCredentials(AuthenticationFlowContext context) {
         return loginForm(context)
                 .setError(Messages.INVALID_USER).createLogin();
     }
 
-    protected Response setDuplicateUserChallenge(AuthenticatorContext context, String eventError, String loginFormError, AuthenticationProcessor.Error authenticatorError) {
+    protected Response setDuplicateUserChallenge(AuthenticationFlowContext context, String eventError, String loginFormError, AuthenticationFlowError authenticatorError) {
         context.getEvent().error(eventError);
         Response challengeResponse = loginForm(context)
                 .setError(loginFormError).createLogin();
@@ -94,18 +94,18 @@ public abstract class AbstractFormAuthenticator implements Authenticator {
         return challengeResponse;
     }
 
-    public boolean invalidUser(AuthenticatorContext context, UserModel user) {
+    public boolean invalidUser(AuthenticationFlowContext context, UserModel user) {
         if (user == null) {
             context.getEvent().error(Errors.USER_NOT_FOUND);
             Response challengeResponse = invalidUser(context);
-            context.failureChallenge(AuthenticationProcessor.Error.INVALID_USER, challengeResponse);
+            context.failureChallenge(AuthenticationFlowError.INVALID_USER, challengeResponse);
             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);
+            context.failureChallenge(AuthenticationFlowError.USER_DISABLED, challengeResponse);
             return true;
         }
         if (context.getRealm().isBruteForceProtected()) {
@@ -113,19 +113,19 @@ public abstract class AbstractFormAuthenticator implements Authenticator {
                 context.getEvent().user(user);
                 context.getEvent().error(Errors.USER_TEMPORARILY_DISABLED);
                 Response challengeResponse = temporarilyDisabledUser(context);
-                context.failureChallenge(AuthenticationProcessor.Error.USER_TEMPORARILY_DISABLED, challengeResponse);
+                context.failureChallenge(AuthenticationFlowError.USER_TEMPORARILY_DISABLED, challengeResponse);
                 return true;
             }
         }
         return false;
     }
 
-    public boolean validateUser(AuthenticatorContext context, MultivaluedMap<String, String> inputData) {
+    public boolean validateUser(AuthenticationFlowContext context, MultivaluedMap<String, String> inputData) {
         String username = inputData.getFirst(AuthenticationManager.FORM_USERNAME);
         if (username == null) {
             context.getEvent().error(Errors.USER_NOT_FOUND);
             Response challengeResponse = invalidUser(context);
-            context.failureChallenge(AuthenticationProcessor.Error.INVALID_USER, challengeResponse);
+            context.failureChallenge(AuthenticationFlowError.INVALID_USER, challengeResponse);
             return false;
         }
         context.getEvent().detail(Details.USERNAME, username);
@@ -139,9 +139,9 @@ public abstract class AbstractFormAuthenticator implements Authenticator {
 
             // Could happen during federation import
             if (mde.getDuplicateFieldName() != null && mde.getDuplicateFieldName().equals(UserModel.EMAIL)) {
-                setDuplicateUserChallenge(context, Errors.EMAIL_IN_USE, Messages.EMAIL_EXISTS, AuthenticationProcessor.Error.INVALID_USER);
+                setDuplicateUserChallenge(context, Errors.EMAIL_IN_USE, Messages.EMAIL_EXISTS, AuthenticationFlowError.INVALID_USER);
             } else {
-                setDuplicateUserChallenge(context, Errors.USERNAME_IN_USE, Messages.USERNAME_EXISTS, AuthenticationProcessor.Error.INVALID_USER);
+                setDuplicateUserChallenge(context, Errors.USERNAME_IN_USE, Messages.USERNAME_EXISTS, AuthenticationFlowError.INVALID_USER);
             }
 
             return false;
@@ -160,7 +160,7 @@ public abstract class AbstractFormAuthenticator implements Authenticator {
         return true;
     }
 
-    public boolean validatePassword(AuthenticatorContext context, MultivaluedMap<String, String> inputData) {
+    public boolean validatePassword(AuthenticationFlowContext context, MultivaluedMap<String, String> inputData) {
         List<UserCredentialModel> credentials = new LinkedList<>();
         String password = inputData.getFirst(CredentialRepresentation.PASSWORD);
         if (password == null || password.isEmpty()) {
@@ -169,7 +169,7 @@ public abstract class AbstractFormAuthenticator implements Authenticator {
             }
             context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
             Response challengeResponse = invalidCredentials(context);
-            context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
+            context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, challengeResponse);
             return false;
         }
         credentials.add(UserCredentialModel.password(password));
@@ -178,7 +178,7 @@ public abstract class AbstractFormAuthenticator implements Authenticator {
             context.getEvent().user(context.getUser());
             context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
             Response challengeResponse = invalidCredentials(context);
-            context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
+            context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, challengeResponse);
             return false;
         }
         return true;
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/CookieAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/CookieAuthenticator.java
index e814191..b2fcf30 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/CookieAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/CookieAuthenticator.java
@@ -1,7 +1,7 @@
 package org.keycloak.authentication.authenticators.browser;
 
+import org.keycloak.authentication.AuthenticationFlowContext;
 import org.keycloak.authentication.Authenticator;
-import org.keycloak.authentication.AuthenticatorContext;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
@@ -19,7 +19,7 @@ public class CookieAuthenticator implements Authenticator {
     }
 
     @Override
-    public void authenticate(AuthenticatorContext context) {
+    public void authenticate(AuthenticationFlowContext context) {
         AuthenticationManager.AuthResult authResult = AuthenticationManager.authenticateIdentityCookie(context.getSession(),
                 context.getRealm(), true);
         if (authResult == null) {
@@ -33,7 +33,7 @@ public class CookieAuthenticator implements Authenticator {
     }
 
     @Override
-    public void action(AuthenticatorContext context) {
+    public void action(AuthenticationFlowContext context) {
 
     }
 
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/OTPFormAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/OTPFormAuthenticator.java
index 6c3b09a..8a61476 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/OTPFormAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/OTPFormAuthenticator.java
@@ -1,8 +1,8 @@
 package org.keycloak.authentication.authenticators.browser;
 
-import org.keycloak.authentication.AuthenticationProcessor;
+import org.keycloak.authentication.AuthenticationFlowError;
+import org.keycloak.authentication.AuthenticationFlowContext;
 import org.keycloak.authentication.Authenticator;
-import org.keycloak.authentication.AuthenticatorContext;
 import org.keycloak.events.Errors;
 import org.keycloak.login.LoginFormsProvider;
 import org.keycloak.models.KeycloakSession;
@@ -26,17 +26,17 @@ public class OTPFormAuthenticator extends AbstractFormAuthenticator implements A
     public static final String TOTP_FORM_ACTION = "totp";
 
     @Override
-    public void action(AuthenticatorContext context) {
+    public void action(AuthenticationFlowContext context) {
         validateOTP(context);
     }
 
     @Override
-    public void authenticate(AuthenticatorContext context) {
+    public void authenticate(AuthenticationFlowContext context) {
         Response challengeResponse = challenge(context, null);
         context.challenge(challengeResponse);
     }
 
-    public void validateOTP(AuthenticatorContext context) {
+    public void validateOTP(AuthenticationFlowContext context) {
         MultivaluedMap<String, String> inputData = context.getHttpRequest().getDecodedFormParameters();
         List<UserCredentialModel> credentials = new LinkedList<>();
         String password = inputData.getFirst(CredentialRepresentation.TOTP);
@@ -51,7 +51,7 @@ public class OTPFormAuthenticator extends AbstractFormAuthenticator implements A
             context.getEvent().user(context.getUser())
                     .error(Errors.INVALID_USER_CREDENTIALS);
             Response challengeResponse = challenge(context, Messages.INVALID_TOTP);
-            context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
+            context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, challengeResponse);
             return;
         }
         context.success();
@@ -62,7 +62,7 @@ public class OTPFormAuthenticator extends AbstractFormAuthenticator implements A
         return true;
     }
 
-    protected Response challenge(AuthenticatorContext context, String error) {
+    protected Response challenge(AuthenticationFlowContext context, String error) {
         String accessCode = context.generateAccessCode();
         URI action = getActionUrl(context, accessCode);
         LoginFormsProvider forms = context.getSession().getProvider(LoginFormsProvider.class)
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticator.java
index 6caa1ab..3e341c9 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticator.java
@@ -2,9 +2,9 @@ package org.keycloak.authentication.authenticators.browser;
 
 import org.jboss.logging.Logger;
 import org.jboss.resteasy.spi.HttpRequest;
-import org.keycloak.authentication.AuthenticationProcessor;
+import org.keycloak.authentication.AuthenticationFlowError;
+import org.keycloak.authentication.AuthenticationFlowContext;
 import org.keycloak.authentication.Authenticator;
-import org.keycloak.authentication.AuthenticatorContext;
 import org.keycloak.constants.KerberosConstants;
 import org.keycloak.events.Errors;
 import org.keycloak.login.LoginFormsProvider;
@@ -35,13 +35,13 @@ public class SpnegoAuthenticator extends AbstractFormAuthenticator implements Au
     }
 
     @Override
-    public void action(AuthenticatorContext context) {
+    public void action(AuthenticationFlowContext context) {
         context.attempted();
         return;
     }
 
     @Override
-    public void authenticate(AuthenticatorContext context) {
+    public void authenticate(AuthenticationFlowContext context) {
         HttpRequest request = context.getHttpRequest();
         String authHeader = request.getHttpHeaders().getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
         if (authHeader == null) {
@@ -62,7 +62,7 @@ public class SpnegoAuthenticator extends AbstractFormAuthenticator implements Au
             return;
         }
         if (tokens.length != 2) {
-            context.failure(AuthenticationProcessor.Error.INVALID_CREDENTIALS);
+            context.failure(AuthenticationFlowError.INVALID_CREDENTIALS);
             return;
         }
 
@@ -85,11 +85,11 @@ public class SpnegoAuthenticator extends AbstractFormAuthenticator implements Au
             context.challenge(challenge);
         } else {
             context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
-            context.failure(AuthenticationProcessor.Error.INVALID_CREDENTIALS);
+            context.failure(AuthenticationFlowError.INVALID_CREDENTIALS);
         }
     }
 
-    private Response challengeNegotiation(AuthenticatorContext context, final String negotiateToken) {
+    private Response challengeNegotiation(AuthenticationFlowContext context, final String negotiateToken) {
         String negotiateHeader = negotiateToken == null ? KerberosConstants.NEGOTIATE : KerberosConstants.NEGOTIATE + " " + negotiateToken;
 
         if (logger.isTraceEnabled()) {
@@ -115,7 +115,7 @@ public class SpnegoAuthenticator extends AbstractFormAuthenticator implements Au
      * @param negotiateHeader
      * @return
      */
-    protected Response optionalChallengeRedirect(AuthenticatorContext context, String negotiateHeader) {
+    protected Response optionalChallengeRedirect(AuthenticationFlowContext context, String negotiateHeader) {
         String accessCode = context.generateAccessCode();
         URI action = getActionUrl(context, accessCode);
 
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordForm.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordForm.java
index d129eaf..d1922d3 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordForm.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordForm.java
@@ -1,16 +1,15 @@
 package org.keycloak.authentication.authenticators.browser;
 
 import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
+import org.keycloak.authentication.AuthenticationFlowContext;
 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.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.protocol.LoginProtocol;
-import org.keycloak.protocol.RestartLoginCookie;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 import org.keycloak.services.managers.AuthenticationManager;
 
@@ -24,7 +23,7 @@ import javax.ws.rs.core.Response;
 public class UsernamePasswordForm extends AbstractFormAuthenticator implements Authenticator {
 
    @Override
-    public void action(AuthenticatorContext context) {
+    public void action(AuthenticationFlowContext context) {
         MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
         if (formData.containsKey("cancel")) {
             context.getEvent().error(Errors.REJECTED_BY_USER);
@@ -42,12 +41,12 @@ public class UsernamePasswordForm extends AbstractFormAuthenticator implements A
         context.success();
     }
 
-    protected boolean validateForm(AuthenticatorContext context, MultivaluedMap<String, String> formData) {
+    protected boolean validateForm(AuthenticationFlowContext context, MultivaluedMap<String, String> formData) {
         return validateUser(context, formData) && validatePassword(context, formData);
     }
 
     @Override
-    public void authenticate(AuthenticatorContext context) {
+    public void authenticate(AuthenticationFlowContext context) {
         MultivaluedMap<String, String> formData = new MultivaluedMapImpl<>();
         String loginHint = context.getClientSession().getNote(OIDCLoginProtocol.LOGIN_HINT_PARAM);
 
@@ -71,7 +70,7 @@ public class UsernamePasswordForm extends AbstractFormAuthenticator implements A
         return false;
     }
 
-    protected Response challenge(AuthenticatorContext context, MultivaluedMap<String, String> formData) {
+    protected Response challenge(AuthenticationFlowContext context, MultivaluedMap<String, String> formData) {
         LoginFormsProvider forms = loginForm(context);
 
         if (formData.size() > 0) forms.setFormData(formData);
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
index 965395f..2faa7bc 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/AbstractDirectGrantAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/AbstractDirectGrantAuthenticator.java
@@ -2,8 +2,8 @@ package org.keycloak.authentication.authenticators.directgrant;
 
 import org.keycloak.Config;
 import org.keycloak.OAuth2Constants;
+import org.keycloak.authentication.AuthenticationFlowContext;
 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;
@@ -28,7 +28,7 @@ public abstract class AbstractDirectGrantAuthenticator implements Authenticator,
     }
 
     @Override
-    public void action(AuthenticatorContext context) {
+    public void action(AuthenticationFlowContext context) {
 
     }
 
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
index 9c9846b..c26da77 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateOTP.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateOTP.java
@@ -1,8 +1,8 @@
 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.AuthenticationFlowError;
+import org.keycloak.authentication.AuthenticationFlowContext;
 import org.keycloak.events.Errors;
 import org.keycloak.models.AuthenticationExecutionModel;
 import org.keycloak.models.KeycloakSession;
@@ -27,14 +27,14 @@ public class ValidateOTP extends AbstractDirectGrantAuthenticator {
     public static final String PROVIDER_ID = "direct-grant-validate-otp";
 
     @Override
-    public void authenticate(AuthenticatorContext context) {
+    public void authenticate(AuthenticationFlowContext 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);
+                context.failure(AuthenticationFlowError.INVALID_USER, challengeResponse);
             }
             return;
         }
@@ -47,7 +47,7 @@ public class ValidateOTP extends AbstractDirectGrantAuthenticator {
             }
             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);
+            context.failure(AuthenticationFlowError.INVALID_USER, challengeResponse);
             return;
         }
         credentials.add(UserCredentialModel.otp(context.getRealm().getOTPPolicy().getType(), otp));
@@ -56,7 +56,7 @@ public class ValidateOTP extends AbstractDirectGrantAuthenticator {
             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);
+            context.failure(AuthenticationFlowError.INVALID_USER, challengeResponse);
             return;
         }
 
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
index 792fd5e..7399eef 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidatePassword.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidatePassword.java
@@ -1,21 +1,16 @@
 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.authentication.AuthenticationFlowError;
+import org.keycloak.authentication.AuthenticationFlowContext;
 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;
@@ -32,7 +27,7 @@ public class ValidatePassword extends AbstractDirectGrantAuthenticator {
     public static final String PROVIDER_ID = "direct-grant-validate-password";
 
     @Override
-    public void authenticate(AuthenticatorContext context) {
+    public void authenticate(AuthenticationFlowContext context) {
         MultivaluedMap<String, String> inputData = context.getHttpRequest().getDecodedFormParameters();
         List<UserCredentialModel> credentials = new LinkedList<>();
         String password = inputData.getFirst(CredentialRepresentation.PASSWORD);
@@ -42,7 +37,7 @@ public class ValidatePassword extends AbstractDirectGrantAuthenticator {
             }
             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);
+            context.failure(AuthenticationFlowError.INVALID_USER, challengeResponse);
             return;
         }
         credentials.add(UserCredentialModel.password(password));
@@ -51,7 +46,7 @@ public class ValidatePassword extends AbstractDirectGrantAuthenticator {
             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);
+            context.failure(AuthenticationFlowError.INVALID_USER, challengeResponse);
             return;
         }
 
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
index 5a9cd80..1c39fff 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateUsername.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateUsername.java
@@ -1,33 +1,24 @@
 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.AuthenticationFlowError;
+import org.keycloak.authentication.AuthenticationFlowContext;
 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 <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -39,13 +30,13 @@ public class ValidateUsername extends AbstractDirectGrantAuthenticator {
     public static final String PROVIDER_ID = "direct-grant-validate-username";
 
     @Override
-    public void authenticate(AuthenticatorContext context) {
+    public void authenticate(AuthenticationFlowContext context) {
         MultivaluedMap<String, String> 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);
+            context.failure(AuthenticationFlowError.INVALID_USER, challengeResponse);
             return;
         }
         context.getEvent().detail(Details.USERNAME, username);
@@ -57,7 +48,7 @@ public class ValidateUsername extends AbstractDirectGrantAuthenticator {
         } 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);
+            context.failure(AuthenticationFlowError.INVALID_USER, challengeResponse);
             return;
         }
 
@@ -65,14 +56,14 @@ public class ValidateUsername extends AbstractDirectGrantAuthenticator {
         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);
+            context.failure(AuthenticationFlowError.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);
+            context.failure(AuthenticationFlowError.INVALID_USER, challengeResponse);
             return;
         }
         if (context.getRealm().isBruteForceProtected()) {
@@ -80,7 +71,7 @@ public class ValidateUsername extends AbstractDirectGrantAuthenticator {
                 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);
+                context.failure(AuthenticationFlowError.INVALID_USER, challengeResponse);
                 return;
             }
         }
diff --git a/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java b/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java
index a84131a..f03f708 100755
--- a/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java
+++ b/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java
@@ -55,14 +55,14 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
             } else if (model.getId().equals(actionExecution)) {
                 AuthenticatorFactory factory = (AuthenticatorFactory) processor.getSession().getKeycloakSessionFactory().getProviderFactory(Authenticator.class, model.getAuthenticator());
                 Authenticator authenticator = factory.create();
-                AuthenticatorContext result = processor.createAuthenticatorContext(model, authenticator, executions);
+                AuthenticationProcessor.Result result = processor.createAuthenticatorContext(model, authenticator, executions);
                 authenticator.action(result);
                 Response response = processResult(result);
                 if (response == null) return processFlow();
                 else return response;
             }
         }
-        throw new AuthenticationProcessor.AuthException("action is not in current execution", AuthenticationProcessor.Error.INTERNAL_ERROR);
+        throw new AuthenticationFlowException("action is not in current execution", AuthenticationFlowError.INTERNAL_ERROR);
     }
 
     @Override
@@ -106,7 +106,7 @@ 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);
+                throw new AuthenticationFlowException("Could not find AuthenticatorFactory for: " + model.getAuthenticator(), AuthenticationFlowError.INTERNAL_ERROR);
             }
             Authenticator authenticator = factory.create();
             AuthenticationProcessor.logger.debugv("authenticator: {0}", factory.getId());
@@ -117,7 +117,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
                     processor.getClientSession().setExecutionStatus(challengedAlternativeExecution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
                     return alternativeChallenge;
                 }
-                throw new AuthenticationProcessor.AuthException("authenticator: " + factory.getId(), AuthenticationProcessor.Error.UNKNOWN_USER);
+                throw new AuthenticationFlowException("authenticator: " + factory.getId(), AuthenticationFlowError.UNKNOWN_USER);
             }
             boolean configuredFor = false;
             if (authenticator.requiresUser() && authUser != null) {
@@ -130,7 +130,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
                             authenticator.setRequiredActions(processor.getSession(), processor.getRealm(), processor.getClientSession().getAuthenticatedUser());
                             continue;
                         } else {
-                            throw new AuthenticationProcessor.AuthException(AuthenticationProcessor.Error.CREDENTIAL_SETUP_REQUIRED);
+                            throw new AuthenticationFlowException(AuthenticationFlowError.CREDENTIAL_SETUP_REQUIRED);
                         }
                     } else if (model.isOptional()) {
                         processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
@@ -138,7 +138,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
                     }
                 }
             }
-            AuthenticatorContext context = processor.createAuthenticatorContext(model, authenticator, executions);
+            AuthenticationProcessor.Result context = processor.createAuthenticatorContext(model, authenticator, executions);
             authenticator.authenticate(context);
             Response response = processResult(context);
             if (response != null) return response;
@@ -147,7 +147,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
     }
 
 
-    public Response processResult(AuthenticatorContext result) {
+    public Response processResult(AuthenticationProcessor.Result result) {
         AuthenticationExecutionModel execution = result.getExecution();
         AuthenticationProcessor.Status status = result.getStatus();
         if (status == AuthenticationProcessor.Status.SUCCESS) {
@@ -162,7 +162,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
             if (result.getChallenge() != null) {
                 return sendChallenge(result, execution);
             }
-            throw new AuthenticationProcessor.AuthException(result.getError());
+            throw new AuthenticationFlowException(result.getError());
         } else if (status == AuthenticationProcessor.Status.FORCE_CHALLENGE) {
             processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
             return sendChallenge(result, execution);
@@ -192,19 +192,19 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
         } else if (status == AuthenticationProcessor.Status.ATTEMPTED) {
             AuthenticationProcessor.logger.debugv("authenticator ATTEMPTED: {0}", execution.getAuthenticator());
             if (execution.getRequirement() == AuthenticationExecutionModel.Requirement.REQUIRED) {
-                throw new AuthenticationProcessor.AuthException(AuthenticationProcessor.Error.INVALID_CREDENTIALS);
+                throw new AuthenticationFlowException(AuthenticationFlowError.INVALID_CREDENTIALS);
             }
             processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.ATTEMPTED);
             return null;
         } else {
             AuthenticationProcessor.logger.debugv("authenticator INTERNAL_ERROR: {0}", execution.getAuthenticator());
             AuthenticationProcessor.logger.error("Unknown result status");
-            throw new AuthenticationProcessor.AuthException(AuthenticationProcessor.Error.INTERNAL_ERROR);
+            throw new AuthenticationFlowException(AuthenticationFlowError.INTERNAL_ERROR);
         }
 
     }
 
-    public Response sendChallenge(AuthenticatorContext result, AuthenticationExecutionModel execution) {
+    public Response sendChallenge(AuthenticationProcessor.Result result, AuthenticationExecutionModel execution) {
         processor.getClientSession().setNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, execution.getId());
         return result.getChallenge();
     }
diff --git a/services/src/main/java/org/keycloak/authentication/FormAction.java b/services/src/main/java/org/keycloak/authentication/FormAction.java
index d555820..3dcdc4e 100755
--- a/services/src/main/java/org/keycloak/authentication/FormAction.java
+++ b/services/src/main/java/org/keycloak/authentication/FormAction.java
@@ -7,14 +7,44 @@ import org.keycloak.models.UserModel;
 import org.keycloak.provider.Provider;
 
 /**
+ * Fine grain processing of a form.  Allows you to split up the processing of a form into smaller parts so that you can
+ * enable/disable them from the admin console.  For example, Recaptcha is a FormAction.  This allows you as the admin
+ * to turn Recaptcha on/off even though it is on the same form/page as other registration validation.
+ *
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
 public interface FormAction extends Provider {
+    /**
+     * This is the first phase of form processing.  Each FormAction.validate() method is called.  This gives the
+     * FormAction a chance to validate and challenge if user input is invalid.
+     *
+     * @param context
+     */
     void validate(ValidationContext context);
+
+    /**
+     * Called after all validate() calls of all FormAction providers are successful.
+     *
+     * @param context
+     */
     void success(FormContext context);
 
+    /**
+     * Does this FormAction require that a user be set? For registration, this method will always return false.
+     *
+     * @return
+     */
     boolean requiresUser();
+
+    /**
+     * Is this FormAction configured for the current user?
+     *
+     * @param session
+     * @param realm
+     * @param user
+     * @return
+     */
     boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user);
 
     /**
@@ -23,6 +53,13 @@ public interface FormAction extends Provider {
      */
     void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user);
 
+    /**
+     * When a FormAuthenticator is rendering the challenge page, even FormAction.buildPage() method will be called
+     * This gives the FormAction the opportunity to add additional attributes to the form to be displayed.
+     *
+     * @param context
+     * @param form
+     */
     void buildPage(FormContext context, LoginFormsProvider form);
 
 }
diff --git a/services/src/main/java/org/keycloak/authentication/FormActionFactory.java b/services/src/main/java/org/keycloak/authentication/FormActionFactory.java
index 8708141..1c3aada 100755
--- a/services/src/main/java/org/keycloak/authentication/FormActionFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/FormActionFactory.java
@@ -3,6 +3,12 @@ package org.keycloak.authentication;
 import org.keycloak.provider.ProviderFactory;
 
 /**
+ * Factory for instantiating FormAction objects.  This is a singleton and created when Keycloak boots.
+ *
+ * You must specify a file
+ * META-INF/services/org.keycloak.authentication.FormActionFactory in the jar that this class is contained in
+ * This file must have the fully qualified class name of all your FormActionFactory classes
+ *
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
diff --git a/services/src/main/java/org/keycloak/authentication/FormAuthenticationFlow.java b/services/src/main/java/org/keycloak/authentication/FormAuthenticationFlow.java
index d244740..f29ad39 100755
--- a/services/src/main/java/org/keycloak/authentication/FormAuthenticationFlow.java
+++ b/services/src/main/java/org/keycloak/authentication/FormAuthenticationFlow.java
@@ -11,9 +11,7 @@ import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
-import org.keycloak.models.UserSessionModel;
 import org.keycloak.models.utils.FormMessage;
-import org.keycloak.services.managers.BruteForceProtector;
 import org.keycloak.services.resources.LoginActionsService;
 
 import javax.ws.rs.core.MultivaluedMap;
@@ -142,7 +140,7 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
     @Override
     public Response processAction(String actionExecution) {
         if (!actionExecution.equals(formExecution.getId())) {
-            throw new AuthenticationProcessor.AuthException("action is not current execution", AuthenticationProcessor.Error.INTERNAL_ERROR);
+            throw new AuthenticationFlowException("action is not current execution", AuthenticationFlowError.INTERNAL_ERROR);
         }
         Map<String, ClientSessionModel.ExecutionStatus> executionStatus = new HashMap<>();
         List<FormAction> requiredActions = new LinkedList<>();
@@ -157,7 +155,7 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
 
             UserModel authUser = processor.getClientSession().getAuthenticatedUser();
             if (action.requiresUser() && authUser == null) {
-                throw new AuthenticationProcessor.AuthException("form action: " + formExecution.getAuthenticator() + " requires user", AuthenticationProcessor.Error.UNKNOWN_USER);
+                throw new AuthenticationFlowException("form action: " + formExecution.getAuthenticator() + " requires user", AuthenticationFlowError.UNKNOWN_USER);
             }
             boolean configuredFor = false;
             if (action.requiresUser() && authUser != null) {
@@ -170,7 +168,7 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
                             requiredActions.add(action);
                             continue;
                         } else {
-                            throw new AuthenticationProcessor.AuthException(AuthenticationProcessor.Error.CREDENTIAL_SETUP_REQUIRED);
+                            throw new AuthenticationFlowException(AuthenticationFlowError.CREDENTIAL_SETUP_REQUIRED);
                         }
                     } else if (formActionExecution.isOptional()) {
                         executionStatus.put(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
diff --git a/services/src/main/java/org/keycloak/authentication/FormAuthenticator.java b/services/src/main/java/org/keycloak/authentication/FormAuthenticator.java
index 5fb0dc0..f31dd30 100755
--- a/services/src/main/java/org/keycloak/authentication/FormAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/FormAuthenticator.java
@@ -9,9 +9,22 @@ import javax.ws.rs.core.Response;
 import java.util.List;
 
 /**
+ * This class is responsible for rendering a form.  The way it works is that each FormAction that is a child of this
+ * FormAuthenticator, will have its buildPage() method call first, then the FormAuthenticator.render() method will be invoked.
+ *
+ * This gives each FormAction a chance to add information to the form in an independent manner.
+ *
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
 public interface FormAuthenticator extends Provider {
+    /**
+     * Called to render the FormAuthenticator's challenge page.  If null is returned, then success is assumed and the
+     * next authenticator in the flow will be invoked.
+     *
+     * @param context
+     * @param form
+     * @return
+     */
     Response render(FormContext context, LoginFormsProvider form);
 }
diff --git a/services/src/main/java/org/keycloak/authentication/FormAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/FormAuthenticatorFactory.java
index d5455f1..d49beed 100755
--- a/services/src/main/java/org/keycloak/authentication/FormAuthenticatorFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/FormAuthenticatorFactory.java
@@ -3,6 +3,12 @@ package org.keycloak.authentication;
 import org.keycloak.provider.ProviderFactory;
 
 /**
+ * Factory for instantiating FormAuthenticators.  This is a singleton and created when Keycloak boots.
+ *
+ * You must specify a file
+ * META-INF/services/org.keycloak.authentication.FormAuthenticatorFactory in the jar that this class is contained in
+ * This file must have the fully qualified class name of all your FormAuthenticatorFactory classes
+ *
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
diff --git a/services/src/main/java/org/keycloak/authentication/FormContext.java b/services/src/main/java/org/keycloak/authentication/FormContext.java
index 21ccb05..efb1f70 100755
--- a/services/src/main/java/org/keycloak/authentication/FormContext.java
+++ b/services/src/main/java/org/keycloak/authentication/FormContext.java
@@ -9,25 +9,93 @@ import org.keycloak.models.ClientSessionModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
+import org.keycloak.services.managers.BruteForceProtector;
 
 import javax.ws.rs.core.UriInfo;
 
 /**
-* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
-* @version $Revision: 1 $
-*/
+ * Interface that encapsulates the current state of the current form being executed
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
 public interface FormContext {
+    /**
+     * Current event builder being used
+     *
+     * @return
+     */
     EventBuilder getEvent();
+
+    /**
+     * Create a refresh new EventBuilder to use within this context
+     *
+     * @return
+     */
     EventBuilder newEvent();
+
+    /**
+     * The current execution in the flow
+     *
+     * @return
+     */
     AuthenticationExecutionModel getExecution();
+
+    /**
+     * Current user attached to this flow.  It can return null if no uesr has been identified yet
+     *
+     * @return
+     */
     UserModel getUser();
+
+    /**
+     * Attach a specific user to this flow.
+     *
+     * @param user
+     */
     void setUser(UserModel user);
+
+    /**
+     * Current realm
+     *
+     * @return
+     */
     RealmModel getRealm();
+
+    /**
+     * ClientSessionModel attached to this flow
+     *
+     * @return
+     */
     ClientSessionModel getClientSession();
+
+    /**
+     * Information about the IP address from the connecting HTTP client.
+     *
+     * @return
+     */
     ClientConnection getConnection();
+
+    /**
+     * UriInfo of the current request
+     *
+     * @return
+     */
     UriInfo getUriInfo();
+
+    /**
+     * Current session
+     *
+     * @return
+     */
     KeycloakSession getSession();
+
     HttpRequest getHttpRequest();
-    AuthenticatorConfigModel getAuthenticatorConfig();
 
+    /**
+     * Get any configuration associated with the current execution
+     *
+     * @return
+     */
+    AuthenticatorConfigModel getAuthenticatorConfig();
 }
diff --git a/services/src/main/java/org/keycloak/authentication/RequiredActionContext.java b/services/src/main/java/org/keycloak/authentication/RequiredActionContext.java
index 8345f48..67404e5 100755
--- a/services/src/main/java/org/keycloak/authentication/RequiredActionContext.java
+++ b/services/src/main/java/org/keycloak/authentication/RequiredActionContext.java
@@ -12,11 +12,24 @@ import org.keycloak.models.UserSessionModel;
 import javax.ws.rs.core.UriInfo;
 
 /**
+ * Interface that encapsulates current information about the current requred action
+ *
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
 public interface RequiredActionContext {
+    /**
+     * Current event builder being used
+     *
+     * @return
+     */
     EventBuilder getEvent();
+
+    /**
+     * Current user
+     *
+     * @return
+     */
     UserModel getUser();
     RealmModel getRealm();
     ClientSessionModel getClientSession();
@@ -25,5 +38,12 @@ public interface RequiredActionContext {
     UriInfo getUriInfo();
     KeycloakSession getSession();
     HttpRequest getHttpRequest();
+
+    /**
+     * Generates access code and updates clientsession timestamp
+     * Access codes must be included in form action callbacks as a query parameter.
+     *
+     * @return
+     */
     String generateAccessCode(String action);
 }
diff --git a/services/src/main/java/org/keycloak/authentication/RequiredActionFactory.java b/services/src/main/java/org/keycloak/authentication/RequiredActionFactory.java
index 120f014..e44b4dc 100755
--- a/services/src/main/java/org/keycloak/authentication/RequiredActionFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/RequiredActionFactory.java
@@ -3,9 +3,19 @@ package org.keycloak.authentication;
 import org.keycloak.provider.ProviderFactory;
 
 /**
+ * You must specify a file
+ * META-INF/services/org.keycloak.authentication.RequiredActionFactory in the jar that this class is contained in
+ * This file must have the fully qualified class name of all your RequiredActionFactory classes
+ *
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
 public interface RequiredActionFactory extends ProviderFactory<RequiredActionProvider> {
+
+    /**
+     * Display text used in admin console to reference this required action
+     *
+     * @return
+     */
     String getDisplayText();
 }
diff --git a/services/src/main/java/org/keycloak/authentication/RequiredActionProvider.java b/services/src/main/java/org/keycloak/authentication/RequiredActionProvider.java
index 81c41a3..4d441cd 100755
--- a/services/src/main/java/org/keycloak/authentication/RequiredActionProvider.java
+++ b/services/src/main/java/org/keycloak/authentication/RequiredActionProvider.java
@@ -5,12 +5,46 @@ import org.keycloak.provider.Provider;
 import javax.ws.rs.core.Response;
 
 /**
+ * RequiredAction provider.  Required actions are one-time actions that a user must perform before they are logged in.
+ *
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
  */
 public interface RequiredActionProvider extends Provider {
+    /**
+     * Called every time a uesr authenticates.  This checks to see if this required action should be triggered.
+     * The implementation of this method is responsible for setting the required action on the UserModel.
+     *
+     * For example, the UpdatePassword required actions checks the password policies to see if the password has expired.
+     *
+     * @param context
+     */
     void evaluateTriggers(RequiredActionContext context);
-    Response invokeRequiredAction(RequiredActionContext context);
+
+    /**
+     * If the user has a required action set, this method will be the initial call to obtain what to display to the
+     * user's browser.  Return null if no action should be done.
+     *
+     * @param context
+     * @return
+     */
+    Response requiredActionChallenge(RequiredActionContext context);
+
+    /**
+     * This is an optional method.  If the required action has a more complex interaction, you can encapsulate it within
+     * a REST service.  This method returns a JAX-RS sub locator object that can be referenced at:
+     *
+     * /realms/{realm}/login-actions/required-actions/{provider-id}
+     *
+     * @param context
+     * @return
+     */
     Object jaxrsService(RequiredActionContext context);
+
+    /**
+     * Provider id of this required action.  Must match ProviderFactory.getId().
+     *
+     * @return
+     */
     String getProviderId();
 }
diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/TermsAndConditions.java b/services/src/main/java/org/keycloak/authentication/requiredactions/TermsAndConditions.java
index 5ad5132..6f094f7 100755
--- a/services/src/main/java/org/keycloak/authentication/requiredactions/TermsAndConditions.java
+++ b/services/src/main/java/org/keycloak/authentication/requiredactions/TermsAndConditions.java
@@ -88,7 +88,7 @@ public class TermsAndConditions implements RequiredActionProvider, RequiredActio
     }
 
     @Override
-    public Response invokeRequiredAction(RequiredActionContext context) {
+    public Response requiredActionChallenge(RequiredActionContext context) {
         return context.getSession().getProvider(LoginFormsProvider.class)
                 .setClientSessionCode(context.generateAccessCode(getProviderId()))
                 .setUser(context.getUser())
diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdatePassword.java b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdatePassword.java
index d59a827..b00510c 100755
--- a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdatePassword.java
+++ b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdatePassword.java
@@ -48,7 +48,7 @@ public class UpdatePassword implements RequiredActionProvider, RequiredActionFac
     }
 
     @Override
-    public Response invokeRequiredAction(RequiredActionContext context) {
+    public Response requiredActionChallenge(RequiredActionContext context) {
         LoginFormsProvider loginFormsProvider = context.getSession()
                 .getProvider(LoginFormsProvider.class)
                 .setClientSessionCode(context.generateAccessCode(getProviderId()))
diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateProfile.java b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateProfile.java
index 842b6e4..5860928 100755
--- a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateProfile.java
+++ b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateProfile.java
@@ -23,7 +23,7 @@ public class UpdateProfile implements RequiredActionProvider, RequiredActionFact
     }
 
     @Override
-    public Response invokeRequiredAction(RequiredActionContext context) {
+    public Response requiredActionChallenge(RequiredActionContext context) {
         LoginFormsProvider loginFormsProvider = context.getSession().getProvider(LoginFormsProvider.class)
                 .setClientSessionCode(context.generateAccessCode(getProviderId()))
                 .setUser(context.getUser());
diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateTotp.java b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateTotp.java
index 38b5e1f..ac4e187 100755
--- a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateTotp.java
+++ b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateTotp.java
@@ -25,7 +25,7 @@ public class UpdateTotp implements RequiredActionProvider, RequiredActionFactory
     }
 
     @Override
-    public Response invokeRequiredAction(RequiredActionContext context) {
+    public Response requiredActionChallenge(RequiredActionContext context) {
          LoginFormsProvider loginFormsProvider = context.getSession().getProvider(LoginFormsProvider.class)
                  .setClientSessionCode(context.generateAccessCode(getProviderId()))
                 .setUser(context.getUser());
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 2d1726a..f8a6d96 100755
--- a/services/src/main/java/org/keycloak/authentication/requiredactions/VerifyEmail.java
+++ b/services/src/main/java/org/keycloak/authentication/requiredactions/VerifyEmail.java
@@ -34,7 +34,7 @@ public class VerifyEmail implements RequiredActionProvider, RequiredActionFactor
         }
     }
     @Override
-    public Response invokeRequiredAction(RequiredActionContext context) {
+    public Response requiredActionChallenge(RequiredActionContext context) {
         if (Validation.isBlank(context.getUser().getEmail())) {
             return null;
         }
diff --git a/services/src/main/java/org/keycloak/authentication/ValidationContext.java b/services/src/main/java/org/keycloak/authentication/ValidationContext.java
index 8ff791d..b0c456e 100755
--- a/services/src/main/java/org/keycloak/authentication/ValidationContext.java
+++ b/services/src/main/java/org/keycloak/authentication/ValidationContext.java
@@ -6,10 +6,24 @@ import javax.ws.rs.core.MultivaluedMap;
 import java.util.List;
 
 /**
-* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
-* @version $Revision: 1 $
-*/
+ * Interface that encapsulates the current validation that is being performed.  Calling success() or validationError()
+ * sets the status of this current validation.
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
 public interface ValidationContext extends FormContext {
+    /**
+     * Mark this validation as having a validation error
+     *
+     * @param formData form data you want to display when the form is refreshed
+     * @param errors error messages to display on the form
+     */
     void validationError(MultivaluedMap<String, String> formData, List<FormMessage> errors);
+
+    /**
+     * Mark this validation as sucessful
+     *
+     */
     void success();
 }
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 3ea7d00..d3ab117 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -7,12 +7,10 @@ import org.keycloak.ClientConnection;
 import org.keycloak.RSATokenVerifier;
 import org.keycloak.VerificationException;
 import org.keycloak.authentication.RequiredActionContext;
-import org.keycloak.authentication.RequiredActionFactory;
 import org.keycloak.authentication.RequiredActionProvider;
 import org.keycloak.broker.provider.IdentityProvider;
 import org.keycloak.events.Details;
 import org.keycloak.events.EventBuilder;
-import org.keycloak.events.EventType;
 import org.keycloak.jose.jws.JWSBuilder;
 import org.keycloak.login.LoginFormsProvider;
 import org.keycloak.models.ClientModel;
@@ -22,26 +20,18 @@ import org.keycloak.models.UserConsentModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.ProtocolMapperModel;
 import org.keycloak.models.RealmModel;
-import org.keycloak.models.RequiredCredentialModel;
 import org.keycloak.models.RoleModel;
-import org.keycloak.models.UserCredentialModel;
-import org.keycloak.models.UserCredentialValueModel;
 import org.keycloak.models.UserModel;
-import org.keycloak.models.UserModel.RequiredAction;
 import org.keycloak.models.UserSessionModel;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.protocol.LoginProtocol;
 import org.keycloak.protocol.RestartLoginCookie;
 import org.keycloak.protocol.oidc.TokenManager;
-import org.keycloak.provider.ProviderFactory;
 import org.keycloak.representations.AccessToken;
-import org.keycloak.representations.idm.CredentialRepresentation;
 import org.keycloak.services.resources.IdentityBrokerService;
-import org.keycloak.services.resources.LoginActionsService;
 import org.keycloak.services.resources.RealmsResource;
 import org.keycloak.services.Urls;
 import org.keycloak.services.util.CookieHelper;
-import org.keycloak.services.validation.Validation;
 import org.keycloak.util.Time;
 
 import javax.ws.rs.core.Cookie;
@@ -52,12 +42,9 @@ import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriInfo;
 
 import java.net.URI;
-import java.util.HashSet;
-import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Set;
-import java.util.concurrent.TimeUnit;
 
 /**
  * Stateless object that manages authentication
@@ -442,7 +429,7 @@ public class AuthenticationManager {
         for (String action : requiredActions) {
             RequiredActionProviderModel model = realm.getRequiredActionProviderByAlias(action);
             RequiredActionProvider actionProvider = session.getProvider(RequiredActionProvider.class, model.getProviderId());
-            Response challenge = actionProvider.invokeRequiredAction(context);
+            Response challenge = actionProvider.requiredActionChallenge(context);
             if (challenge != null) {
                 return challenge;
             }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/PassThroughAuthenticator.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/PassThroughAuthenticator.java
index 264eaef..1b83d29 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/PassThroughAuthenticator.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/PassThroughAuthenticator.java
@@ -1,9 +1,9 @@
 package org.keycloak.testsuite.forms;
 
 import org.keycloak.Config;
-import org.keycloak.authentication.AuthenticationProcessor;
+import org.keycloak.authentication.AuthenticationFlowError;
+import org.keycloak.authentication.AuthenticationFlowContext;
 import org.keycloak.authentication.Authenticator;
-import org.keycloak.authentication.AuthenticatorContext;
 import org.keycloak.authentication.AuthenticatorFactory;
 import org.keycloak.models.AuthenticationExecutionModel;
 import org.keycloak.models.KeycloakSession;
@@ -24,10 +24,10 @@ public class PassThroughAuthenticator implements Authenticator, AuthenticatorFac
     public static String username = "test-user@localhost";
 
     @Override
-    public void authenticate(AuthenticatorContext context) {
+    public void authenticate(AuthenticationFlowContext context) {
         UserModel user = KeycloakModelUtils.findUserByNameOrEmail(context.getSession(), context.getRealm(), username);
         if (user == null) {
-            context.failure(AuthenticationProcessor.Error.UNKNOWN_USER);
+            context.failure(AuthenticationFlowError.UNKNOWN_USER);
             return;
         }
         context.setUser(user);
@@ -50,7 +50,7 @@ public class PassThroughAuthenticator implements Authenticator, AuthenticatorFac
     }
 
     @Override
-    public void action(AuthenticatorContext context) {
+    public void action(AuthenticationFlowContext context) {
 
     }
 
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/PassThroughRegistration.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/PassThroughRegistration.java
index 6f8498f..0616ee6 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/PassThroughRegistration.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/forms/PassThroughRegistration.java
@@ -1,9 +1,8 @@
 package org.keycloak.testsuite.forms;
 
 import org.keycloak.Config;
-import org.keycloak.authentication.AuthenticationProcessor;
+import org.keycloak.authentication.AuthenticationFlowContext;
 import org.keycloak.authentication.Authenticator;
-import org.keycloak.authentication.AuthenticatorContext;
 import org.keycloak.authentication.AuthenticatorFactory;
 import org.keycloak.events.Details;
 import org.keycloak.events.EventType;
@@ -12,10 +11,8 @@ import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.KeycloakSessionFactory;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
-import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 import org.keycloak.provider.ProviderConfigProperty;
-import org.keycloak.services.resources.AttributeFormDataProcessor;
 
 import java.util.List;
 
@@ -29,7 +26,7 @@ public class PassThroughRegistration implements Authenticator, AuthenticatorFact
     public static String email = "new-user@localhost";
 
     @Override
-    public void authenticate(AuthenticatorContext context) {
+    public void authenticate(AuthenticationFlowContext context) {
         context.getEvent().detail(Details.USERNAME, username)
                 .detail(Details.REGISTER_METHOD, "form")
                 .detail(Details.EMAIL, email)
@@ -69,7 +66,7 @@ public class PassThroughRegistration implements Authenticator, AuthenticatorFact
     }
 
     @Override
-    public void action(AuthenticatorContext context) {
+    public void action(AuthenticationFlowContext context) {
 
     }