keycloak-memoizeit
Changes
services/src/main/java/org/keycloak/authentication/authenticators/browser/AbstractFormAuthenticator.java 42(+21 -21)
services/src/main/java/org/keycloak/authentication/authenticators/browser/CookieAuthenticator.java 6(+3 -3)
services/src/main/java/org/keycloak/authentication/authenticators/browser/OTPFormAuthenticator.java 14(+7 -7)
services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticator.java 16(+8 -8)
services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordForm.java 11(+5 -6)
services/src/main/java/org/keycloak/authentication/authenticators/directgrant/AbstractDirectGrantAuthenticator.java 4(+2 -2)
services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateOTP.java 12(+6 -6)
services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidatePassword.java 15(+5 -10)
services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateUsername.java 25(+8 -17)
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) {
}