document auth spi
This commit is contained in:
parent
d68603e4ea
commit
5469db311d
35 changed files with 605 additions and 324 deletions
|
@ -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();
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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() {
|
public RealmModel getRealm() {
|
||||||
return realm;
|
return realm;
|
||||||
}
|
}
|
||||||
|
@ -176,19 +163,19 @@ public class AuthenticationProcessor {
|
||||||
public void setAutheticatedUser(UserModel user) {
|
public void setAutheticatedUser(UserModel user) {
|
||||||
UserModel previousUser = clientSession.getAuthenticatedUser();
|
UserModel previousUser = clientSession.getAuthenticatedUser();
|
||||||
if (previousUser != null && !user.getId().equals(previousUser.getId()))
|
if (previousUser != null && !user.getId().equals(previousUser.getId()))
|
||||||
throw new AuthException(Error.USER_CONFLICT);
|
throw new AuthenticationFlowException(AuthenticationFlowError.USER_CONFLICT);
|
||||||
validateUser(user);
|
validateUser(user);
|
||||||
getClientSession().setAuthenticatedUser(user);
|
getClientSession().setAuthenticatedUser(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class Result implements AuthenticatorContext {
|
public class Result implements AuthenticationFlowContext {
|
||||||
AuthenticatorConfigModel authenticatorConfig;
|
AuthenticatorConfigModel authenticatorConfig;
|
||||||
AuthenticationExecutionModel execution;
|
AuthenticationExecutionModel execution;
|
||||||
Authenticator authenticator;
|
Authenticator authenticator;
|
||||||
Status status;
|
Status status;
|
||||||
Response challenge;
|
Response challenge;
|
||||||
Error error;
|
AuthenticationFlowError error;
|
||||||
List<AuthenticationExecutionModel> currentExecutions;
|
List<AuthenticationExecutionModel> currentExecutions;
|
||||||
|
|
||||||
private Result(AuthenticationExecutionModel execution, Authenticator authenticator, List<AuthenticationExecutionModel> currentExecutions) {
|
private Result(AuthenticationExecutionModel execution, Authenticator authenticator, List<AuthenticationExecutionModel> currentExecutions) {
|
||||||
|
@ -220,11 +207,6 @@ public class AuthenticationProcessor {
|
||||||
return execution;
|
return execution;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setExecution(AuthenticationExecutionModel execution) {
|
|
||||||
this.execution = execution;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthenticatorConfigModel getAuthenticatorConfig() {
|
public AuthenticatorConfigModel getAuthenticatorConfig() {
|
||||||
if (execution.getAuthenticatorConfig() == null) return null;
|
if (execution.getAuthenticatorConfig() == null) return null;
|
||||||
|
@ -233,17 +215,10 @@ public class AuthenticationProcessor {
|
||||||
return authenticatorConfig;
|
return authenticatorConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Authenticator getAuthenticator() {
|
public Authenticator getAuthenticator() {
|
||||||
return authenticator;
|
return authenticator;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setAuthenticator(Authenticator authenticator) {
|
|
||||||
this.authenticator = authenticator;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Status getStatus() {
|
public Status getStatus() {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
@ -254,7 +229,7 @@ public class AuthenticationProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void failure(Error error) {
|
public void failure(AuthenticationFlowError error) {
|
||||||
status = Status.FAILED;
|
status = Status.FAILED;
|
||||||
this.error = error;
|
this.error = error;
|
||||||
|
|
||||||
|
@ -275,7 +250,7 @@ public class AuthenticationProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void failureChallenge(Error error, Response challenge) {
|
public void failureChallenge(AuthenticationFlowError error, Response challenge) {
|
||||||
this.error = error;
|
this.error = error;
|
||||||
this.status = Status.FAILURE_CHALLENGE;
|
this.status = Status.FAILURE_CHALLENGE;
|
||||||
this.challenge = challenge;
|
this.challenge = challenge;
|
||||||
|
@ -283,7 +258,7 @@ public class AuthenticationProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void failure(Error error, Response challenge) {
|
public void failure(AuthenticationFlowError error, Response challenge) {
|
||||||
this.error = error;
|
this.error = error;
|
||||||
this.status = Status.FAILED;
|
this.status = Status.FAILED;
|
||||||
this.challenge = challenge;
|
this.challenge = challenge;
|
||||||
|
@ -362,45 +337,11 @@ public class AuthenticationProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Response getChallenge() {
|
public Response getChallenge() {
|
||||||
return challenge;
|
return challenge;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public AuthenticationFlowError getError() {
|
||||||
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() {
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -425,24 +366,24 @@ public class AuthenticationProcessor {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response handleBrowserException(Exception failure) {
|
public Response handleBrowserException(Exception failure) {
|
||||||
if (failure instanceof AuthException) {
|
if (failure instanceof AuthenticationFlowException) {
|
||||||
AuthException e = (AuthException) failure;
|
AuthenticationFlowException e = (AuthenticationFlowException) failure;
|
||||||
logger.error("failed authentication: " + e.getError().toString(), e);
|
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);
|
event.error(Errors.USER_NOT_FOUND);
|
||||||
return ErrorPage.error(session, Messages.INVALID_USER);
|
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);
|
event.error(Errors.USER_DISABLED);
|
||||||
return ErrorPage.error(session, Messages.ACCOUNT_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);
|
event.error(Errors.USER_TEMPORARILY_DISABLED);
|
||||||
return ErrorPage.error(session, Messages.ACCOUNT_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);
|
event.error(Errors.INVALID_CODE);
|
||||||
return ErrorPage.error(session, Messages.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);
|
event.error(Errors.EXPIRED_CODE);
|
||||||
return ErrorPage.error(session, Messages.EXPIRED_CODE);
|
return ErrorPage.error(session, Messages.EXPIRED_CODE);
|
||||||
|
|
||||||
|
@ -463,7 +404,7 @@ public class AuthenticationProcessor {
|
||||||
AuthenticationFlowModel flow = realm.getAuthenticationFlowById(flowId);
|
AuthenticationFlowModel flow = realm.getAuthenticationFlowById(flowId);
|
||||||
if (flow == null) {
|
if (flow == null) {
|
||||||
logger.error("Unknown flow to execute with");
|
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)) {
|
if (flow.getProviderId() == null || flow.getProviderId().equals(AuthenticationFlow.BASIC_FLOW)) {
|
||||||
DefaultAuthenticationFlow flowExecution = new DefaultAuthenticationFlow(this, flow);
|
DefaultAuthenticationFlow flowExecution = new DefaultAuthenticationFlow(this, flow);
|
||||||
|
@ -473,10 +414,10 @@ public class AuthenticationProcessor {
|
||||||
FormAuthenticationFlow flowExecution = new FormAuthenticationFlow(this, execution);
|
FormAuthenticationFlow flowExecution = new FormAuthenticationFlow(this, execution);
|
||||||
return flowExecution;
|
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();
|
checkClientSession();
|
||||||
logger.debug("AUTHENTICATE");
|
logger.debug("AUTHENTICATE");
|
||||||
event.client(clientSession.getClient().getClientId())
|
event.client(clientSession.getClient().getClientId())
|
||||||
|
@ -492,7 +433,7 @@ public class AuthenticationProcessor {
|
||||||
Response challenge = authenticationFlow.processFlow();
|
Response challenge = authenticationFlow.processFlow();
|
||||||
if (challenge != null) return challenge;
|
if (challenge != null) return challenge;
|
||||||
if (clientSession.getAuthenticatedUser() == null) {
|
if (clientSession.getAuthenticatedUser() == null) {
|
||||||
throw new AuthException(Error.UNKNOWN_USER);
|
throw new AuthenticationFlowException(AuthenticationFlowError.UNKNOWN_USER);
|
||||||
}
|
}
|
||||||
return authenticationComplete();
|
return authenticationComplete();
|
||||||
}
|
}
|
||||||
|
@ -533,7 +474,7 @@ public class AuthenticationProcessor {
|
||||||
Response challenge = authenticationFlow.processAction(execution);
|
Response challenge = authenticationFlow.processAction(execution);
|
||||||
if (challenge != null) return challenge;
|
if (challenge != null) return challenge;
|
||||||
if (clientSession.getAuthenticatedUser() == null) {
|
if (clientSession.getAuthenticatedUser() == null) {
|
||||||
throw new AuthException(Error.UNKNOWN_USER);
|
throw new AuthenticationFlowException(AuthenticationFlowError.UNKNOWN_USER);
|
||||||
}
|
}
|
||||||
return authenticationComplete();
|
return authenticationComplete();
|
||||||
}
|
}
|
||||||
|
@ -541,15 +482,15 @@ public class AuthenticationProcessor {
|
||||||
public void checkClientSession() {
|
public void checkClientSession() {
|
||||||
ClientSessionCode code = new ClientSessionCode(realm, clientSession);
|
ClientSessionCode code = new ClientSessionCode(realm, clientSession);
|
||||||
if (!code.isValidAction(ClientSessionModel.Action.AUTHENTICATE.name())) {
|
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())) {
|
if (!code.isActionActive(ClientSessionModel.Action.AUTHENTICATE.name())) {
|
||||||
throw new AuthException(Error.EXPIRED_CODE);
|
throw new AuthenticationFlowException(AuthenticationFlowError.EXPIRED_CODE);
|
||||||
}
|
}
|
||||||
clientSession.setTimestamp(Time.currentTime());
|
clientSession.setTimestamp(Time.currentTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Response authenticateOnly() throws AuthException {
|
public Response authenticateOnly() throws AuthenticationFlowException {
|
||||||
checkClientSession();
|
checkClientSession();
|
||||||
event.client(clientSession.getClient().getClientId())
|
event.client(clientSession.getClient().getClientId())
|
||||||
.detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
|
.detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
|
||||||
|
@ -599,10 +540,10 @@ public class AuthenticationProcessor {
|
||||||
|
|
||||||
public void validateUser(UserModel authenticatedUser) {
|
public void validateUser(UserModel authenticatedUser) {
|
||||||
if (authenticatedUser == null) return;
|
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 (realm.isBruteForceProtected()) {
|
||||||
if (protector.isTemporarilyDisabled(session, realm, authenticatedUser.getUsername())) {
|
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);
|
return new Result(model, authenticator, executions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,11 +15,33 @@ import org.keycloak.provider.Provider;
|
||||||
public interface Authenticator extends 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
|
* @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?
|
* 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);
|
void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user);
|
||||||
|
|
||||||
/**
|
|
||||||
* Usually implements a form action.
|
|
||||||
*
|
|
||||||
* @param context
|
|
||||||
*/
|
|
||||||
void action(AuthenticatorContext context);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,87 +0,0 @@
|
||||||
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 AuthenticatorContext {
|
|
||||||
EventBuilder getEvent();
|
|
||||||
EventBuilder newEvent();
|
|
||||||
|
|
||||||
AuthenticationExecutionModel getExecution();
|
|
||||||
|
|
||||||
void setExecution(AuthenticationExecutionModel execution);
|
|
||||||
|
|
||||||
UserModel getUser();
|
|
||||||
|
|
||||||
void setUser(UserModel user);
|
|
||||||
|
|
||||||
RealmModel getRealm();
|
|
||||||
|
|
||||||
ClientSessionModel getClientSession();
|
|
||||||
void attachUserSession(UserSessionModel userSession);
|
|
||||||
|
|
||||||
ClientConnection getConnection();
|
|
||||||
|
|
||||||
UriInfo getUriInfo();
|
|
||||||
|
|
||||||
KeycloakSession getSession();
|
|
||||||
|
|
||||||
HttpRequest getHttpRequest();
|
|
||||||
BruteForceProtector getProtector();
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
String generateAccessCode();
|
|
||||||
|
|
||||||
AuthenticatorConfigModel getAuthenticatorConfig();
|
|
||||||
|
|
||||||
Authenticator getAuthenticator();
|
|
||||||
|
|
||||||
void setAuthenticator(Authenticator authenticator);
|
|
||||||
|
|
||||||
AuthenticationProcessor.Status getStatus();
|
|
||||||
|
|
||||||
AuthenticationExecutionModel.Requirement getCategoryRequirementFromCurrentFlow(String authenticatorCategory);
|
|
||||||
|
|
||||||
void success();
|
|
||||||
void failure(AuthenticationProcessor.Error error);
|
|
||||||
void failure(AuthenticationProcessor.Error error, Response response);
|
|
||||||
void challenge(Response challenge);
|
|
||||||
|
|
||||||
void forceChallenge(Response challenge);
|
|
||||||
|
|
||||||
void failureChallenge(AuthenticationProcessor.Error error, Response challenge);
|
|
||||||
void attempted();
|
|
||||||
|
|
||||||
|
|
||||||
Response getChallenge();
|
|
||||||
|
|
||||||
AuthenticationProcessor.Error getError();
|
|
||||||
}
|
|
|
@ -6,7 +6,7 @@ import org.keycloak.provider.ConfiguredProvider;
|
||||||
import org.keycloak.provider.ProviderFactory;
|
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
|
* You must specify a file
|
||||||
* META-INF/services/org.keycloak.authentication.AuthenticatorFactory in the jar that this class is contained in
|
* META-INF/services/org.keycloak.authentication.AuthenticatorFactory in the jar that this class is contained in
|
||||||
|
|
|
@ -55,14 +55,14 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
|
||||||
} else if (model.getId().equals(actionExecution)) {
|
} else if (model.getId().equals(actionExecution)) {
|
||||||
AuthenticatorFactory factory = (AuthenticatorFactory) processor.getSession().getKeycloakSessionFactory().getProviderFactory(Authenticator.class, model.getAuthenticator());
|
AuthenticatorFactory factory = (AuthenticatorFactory) processor.getSession().getKeycloakSessionFactory().getProviderFactory(Authenticator.class, model.getAuthenticator());
|
||||||
Authenticator authenticator = factory.create();
|
Authenticator authenticator = factory.create();
|
||||||
AuthenticatorContext result = processor.createAuthenticatorContext(model, authenticator, executions);
|
AuthenticationProcessor.Result result = processor.createAuthenticatorContext(model, authenticator, executions);
|
||||||
authenticator.action(result);
|
authenticator.action(result);
|
||||||
Response response = processResult(result);
|
Response response = processResult(result);
|
||||||
if (response == null) return processFlow();
|
if (response == null) return processFlow();
|
||||||
else return response;
|
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
|
@Override
|
||||||
|
@ -106,7 +106,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
|
||||||
|
|
||||||
AuthenticatorFactory factory = (AuthenticatorFactory) processor.getSession().getKeycloakSessionFactory().getProviderFactory(Authenticator.class, model.getAuthenticator());
|
AuthenticatorFactory factory = (AuthenticatorFactory) processor.getSession().getKeycloakSessionFactory().getProviderFactory(Authenticator.class, model.getAuthenticator());
|
||||||
if (factory == null) {
|
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();
|
Authenticator authenticator = factory.create();
|
||||||
AuthenticationProcessor.logger.debugv("authenticator: {0}", factory.getId());
|
AuthenticationProcessor.logger.debugv("authenticator: {0}", factory.getId());
|
||||||
|
@ -117,7 +117,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
|
||||||
processor.getClientSession().setExecutionStatus(challengedAlternativeExecution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
|
processor.getClientSession().setExecutionStatus(challengedAlternativeExecution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
|
||||||
return alternativeChallenge;
|
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;
|
boolean configuredFor = false;
|
||||||
if (authenticator.requiresUser() && authUser != null) {
|
if (authenticator.requiresUser() && authUser != null) {
|
||||||
|
@ -130,7 +130,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
|
||||||
authenticator.setRequiredActions(processor.getSession(), processor.getRealm(), processor.getClientSession().getAuthenticatedUser());
|
authenticator.setRequiredActions(processor.getSession(), processor.getRealm(), processor.getClientSession().getAuthenticatedUser());
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
throw new AuthenticationProcessor.AuthException(AuthenticationProcessor.Error.CREDENTIAL_SETUP_REQUIRED);
|
throw new AuthenticationFlowException(AuthenticationFlowError.CREDENTIAL_SETUP_REQUIRED);
|
||||||
}
|
}
|
||||||
} else if (model.isOptional()) {
|
} else if (model.isOptional()) {
|
||||||
processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
|
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);
|
authenticator.authenticate(context);
|
||||||
Response response = processResult(context);
|
Response response = processResult(context);
|
||||||
if (response != null) return response;
|
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();
|
AuthenticationExecutionModel execution = result.getExecution();
|
||||||
AuthenticationProcessor.Status status = result.getStatus();
|
AuthenticationProcessor.Status status = result.getStatus();
|
||||||
if (status == AuthenticationProcessor.Status.SUCCESS) {
|
if (status == AuthenticationProcessor.Status.SUCCESS) {
|
||||||
|
@ -162,7 +162,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
|
||||||
if (result.getChallenge() != null) {
|
if (result.getChallenge() != null) {
|
||||||
return sendChallenge(result, execution);
|
return sendChallenge(result, execution);
|
||||||
}
|
}
|
||||||
throw new AuthenticationProcessor.AuthException(result.getError());
|
throw new AuthenticationFlowException(result.getError());
|
||||||
} else if (status == AuthenticationProcessor.Status.FORCE_CHALLENGE) {
|
} else if (status == AuthenticationProcessor.Status.FORCE_CHALLENGE) {
|
||||||
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
|
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
|
||||||
return sendChallenge(result, execution);
|
return sendChallenge(result, execution);
|
||||||
|
@ -192,19 +192,19 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
|
||||||
} else if (status == AuthenticationProcessor.Status.ATTEMPTED) {
|
} else if (status == AuthenticationProcessor.Status.ATTEMPTED) {
|
||||||
AuthenticationProcessor.logger.debugv("authenticator ATTEMPTED: {0}", execution.getAuthenticator());
|
AuthenticationProcessor.logger.debugv("authenticator ATTEMPTED: {0}", execution.getAuthenticator());
|
||||||
if (execution.getRequirement() == AuthenticationExecutionModel.Requirement.REQUIRED) {
|
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);
|
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.ATTEMPTED);
|
||||||
return null;
|
return null;
|
||||||
} else {
|
} else {
|
||||||
AuthenticationProcessor.logger.debugv("authenticator INTERNAL_ERROR: {0}", execution.getAuthenticator());
|
AuthenticationProcessor.logger.debugv("authenticator INTERNAL_ERROR: {0}", execution.getAuthenticator());
|
||||||
AuthenticationProcessor.logger.error("Unknown result status");
|
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());
|
processor.getClientSession().setNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, execution.getId());
|
||||||
return result.getChallenge();
|
return result.getChallenge();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,14 +7,44 @@ import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.provider.Provider;
|
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>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public interface FormAction extends Provider {
|
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);
|
void validate(ValidationContext context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called after all validate() calls of all FormAction providers are successful.
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
*/
|
||||||
void success(FormContext 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();
|
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);
|
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);
|
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);
|
void buildPage(FormContext context, LoginFormsProvider form);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,12 @@ package org.keycloak.authentication;
|
||||||
import org.keycloak.provider.ProviderFactory;
|
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>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -11,9 +11,7 @@ import org.keycloak.models.ClientSessionModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserSessionModel;
|
|
||||||
import org.keycloak.models.utils.FormMessage;
|
import org.keycloak.models.utils.FormMessage;
|
||||||
import org.keycloak.services.managers.BruteForceProtector;
|
|
||||||
import org.keycloak.services.resources.LoginActionsService;
|
import org.keycloak.services.resources.LoginActionsService;
|
||||||
|
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
|
@ -142,7 +140,7 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
|
||||||
@Override
|
@Override
|
||||||
public Response processAction(String actionExecution) {
|
public Response processAction(String actionExecution) {
|
||||||
if (!actionExecution.equals(formExecution.getId())) {
|
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<>();
|
Map<String, ClientSessionModel.ExecutionStatus> executionStatus = new HashMap<>();
|
||||||
List<FormAction> requiredActions = new LinkedList<>();
|
List<FormAction> requiredActions = new LinkedList<>();
|
||||||
|
@ -157,7 +155,7 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
|
||||||
|
|
||||||
UserModel authUser = processor.getClientSession().getAuthenticatedUser();
|
UserModel authUser = processor.getClientSession().getAuthenticatedUser();
|
||||||
if (action.requiresUser() && authUser == null) {
|
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;
|
boolean configuredFor = false;
|
||||||
if (action.requiresUser() && authUser != null) {
|
if (action.requiresUser() && authUser != null) {
|
||||||
|
@ -170,7 +168,7 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
|
||||||
requiredActions.add(action);
|
requiredActions.add(action);
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
throw new AuthenticationProcessor.AuthException(AuthenticationProcessor.Error.CREDENTIAL_SETUP_REQUIRED);
|
throw new AuthenticationFlowException(AuthenticationFlowError.CREDENTIAL_SETUP_REQUIRED);
|
||||||
}
|
}
|
||||||
} else if (formActionExecution.isOptional()) {
|
} else if (formActionExecution.isOptional()) {
|
||||||
executionStatus.put(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
|
executionStatus.put(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
|
||||||
|
|
|
@ -9,9 +9,22 @@ import javax.ws.rs.core.Response;
|
||||||
import java.util.List;
|
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>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public interface FormAuthenticator extends Provider {
|
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);
|
Response render(FormContext context, LoginFormsProvider form);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,12 @@ package org.keycloak.authentication;
|
||||||
import org.keycloak.provider.ProviderFactory;
|
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>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -9,25 +9,93 @@ import org.keycloak.models.ClientSessionModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.services.managers.BruteForceProtector;
|
||||||
|
|
||||||
import javax.ws.rs.core.UriInfo;
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* Interface that encapsulates the current state of the current form being executed
|
||||||
* @version $Revision: 1 $
|
*
|
||||||
*/
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
public interface FormContext {
|
public interface FormContext {
|
||||||
|
/**
|
||||||
|
* Current event builder being used
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
EventBuilder getEvent();
|
EventBuilder getEvent();
|
||||||
EventBuilder newEvent();
|
|
||||||
AuthenticationExecutionModel getExecution();
|
|
||||||
UserModel getUser();
|
|
||||||
void setUser(UserModel user);
|
|
||||||
RealmModel getRealm();
|
|
||||||
ClientSessionModel getClientSession();
|
|
||||||
ClientConnection getConnection();
|
|
||||||
UriInfo getUriInfo();
|
|
||||||
KeycloakSession getSession();
|
|
||||||
HttpRequest getHttpRequest();
|
|
||||||
AuthenticatorConfigModel getAuthenticatorConfig();
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get any configuration associated with the current execution
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
AuthenticatorConfigModel getAuthenticatorConfig();
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,11 +12,24 @@ import org.keycloak.models.UserSessionModel;
|
||||||
import javax.ws.rs.core.UriInfo;
|
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>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public interface RequiredActionContext {
|
public interface RequiredActionContext {
|
||||||
|
/**
|
||||||
|
* Current event builder being used
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
EventBuilder getEvent();
|
EventBuilder getEvent();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current user
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
UserModel getUser();
|
UserModel getUser();
|
||||||
RealmModel getRealm();
|
RealmModel getRealm();
|
||||||
ClientSessionModel getClientSession();
|
ClientSessionModel getClientSession();
|
||||||
|
@ -25,5 +38,12 @@ public interface RequiredActionContext {
|
||||||
UriInfo getUriInfo();
|
UriInfo getUriInfo();
|
||||||
KeycloakSession getSession();
|
KeycloakSession getSession();
|
||||||
HttpRequest getHttpRequest();
|
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);
|
String generateAccessCode(String action);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,19 @@ package org.keycloak.authentication;
|
||||||
import org.keycloak.provider.ProviderFactory;
|
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>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public interface RequiredActionFactory extends ProviderFactory<RequiredActionProvider> {
|
public interface RequiredActionFactory extends ProviderFactory<RequiredActionProvider> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display text used in admin console to reference this required action
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
String getDisplayText();
|
String getDisplayText();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,46 @@ import org.keycloak.provider.Provider;
|
||||||
import javax.ws.rs.core.Response;
|
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>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public interface RequiredActionProvider extends Provider {
|
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);
|
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);
|
Object jaxrsService(RequiredActionContext context);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provider id of this required action. Must match ProviderFactory.getId().
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
String getProviderId();
|
String getProviderId();
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,10 +6,24 @@ import javax.ws.rs.core.MultivaluedMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* Interface that encapsulates the current validation that is being performed. Calling success() or validationError()
|
||||||
* @version $Revision: 1 $
|
* 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 {
|
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);
|
void validationError(MultivaluedMap<String, String> formData, List<FormMessage> errors);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mark this validation as sucessful
|
||||||
|
*
|
||||||
|
*/
|
||||||
void success();
|
void success();
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,9 @@ package org.keycloak.authentication.authenticators.browser;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.OAuth2Constants;
|
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.Authenticator;
|
||||||
import org.keycloak.authentication.AuthenticatorContext;
|
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
import org.keycloak.events.Errors;
|
import org.keycloak.events.Errors;
|
||||||
import org.keycloak.login.LoginFormsProvider;
|
import org.keycloak.login.LoginFormsProvider;
|
||||||
|
@ -36,7 +36,7 @@ public abstract class AbstractFormAuthenticator implements Authenticator {
|
||||||
public static final String ATTEMPTED_USERNAME = "ATTEMPTED_USERNAME";
|
public static final String ATTEMPTED_USERNAME = "ATTEMPTED_USERNAME";
|
||||||
|
|
||||||
@Override
|
@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();
|
String accessCode = context.generateAccessCode();
|
||||||
URI action = getActionUrl(context, accessCode);
|
URI action = getActionUrl(context, accessCode);
|
||||||
LoginFormsProvider provider = context.getSession().getProvider(LoginFormsProvider.class)
|
LoginFormsProvider provider = context.getSession().getProvider(LoginFormsProvider.class)
|
||||||
|
@ -58,35 +58,35 @@ public abstract class AbstractFormAuthenticator implements Authenticator {
|
||||||
return provider;
|
return provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public URI getActionUrl(AuthenticatorContext context, String code) {
|
public URI getActionUrl(AuthenticationFlowContext context, String code) {
|
||||||
return LoginActionsService.authenticationFormProcessor(context.getUriInfo())
|
return LoginActionsService.authenticationFormProcessor(context.getUriInfo())
|
||||||
.queryParam(OAuth2Constants.CODE, code)
|
.queryParam(OAuth2Constants.CODE, code)
|
||||||
.queryParam(EXECUTION, context.getExecution().getId())
|
.queryParam(EXECUTION, context.getExecution().getId())
|
||||||
.build(context.getRealm().getName());
|
.build(context.getRealm().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Response invalidUser(AuthenticatorContext context) {
|
protected Response invalidUser(AuthenticationFlowContext context) {
|
||||||
return loginForm(context)
|
return loginForm(context)
|
||||||
.setError(Messages.INVALID_USER)
|
.setError(Messages.INVALID_USER)
|
||||||
.createLogin();
|
.createLogin();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Response disabledUser(AuthenticatorContext context) {
|
protected Response disabledUser(AuthenticationFlowContext context) {
|
||||||
return loginForm(context)
|
return loginForm(context)
|
||||||
.setError(Messages.ACCOUNT_DISABLED).createLogin();
|
.setError(Messages.ACCOUNT_DISABLED).createLogin();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Response temporarilyDisabledUser(AuthenticatorContext context) {
|
protected Response temporarilyDisabledUser(AuthenticationFlowContext context) {
|
||||||
return loginForm(context)
|
return loginForm(context)
|
||||||
.setError(Messages.ACCOUNT_TEMPORARILY_DISABLED).createLogin();
|
.setError(Messages.ACCOUNT_TEMPORARILY_DISABLED).createLogin();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Response invalidCredentials(AuthenticatorContext context) {
|
protected Response invalidCredentials(AuthenticationFlowContext context) {
|
||||||
return loginForm(context)
|
return loginForm(context)
|
||||||
.setError(Messages.INVALID_USER).createLogin();
|
.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);
|
context.getEvent().error(eventError);
|
||||||
Response challengeResponse = loginForm(context)
|
Response challengeResponse = loginForm(context)
|
||||||
.setError(loginFormError).createLogin();
|
.setError(loginFormError).createLogin();
|
||||||
|
@ -94,18 +94,18 @@ public abstract class AbstractFormAuthenticator implements Authenticator {
|
||||||
return challengeResponse;
|
return challengeResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean invalidUser(AuthenticatorContext context, UserModel user) {
|
public boolean invalidUser(AuthenticationFlowContext context, UserModel user) {
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
context.getEvent().error(Errors.USER_NOT_FOUND);
|
context.getEvent().error(Errors.USER_NOT_FOUND);
|
||||||
Response challengeResponse = invalidUser(context);
|
Response challengeResponse = invalidUser(context);
|
||||||
context.failureChallenge(AuthenticationProcessor.Error.INVALID_USER, challengeResponse);
|
context.failureChallenge(AuthenticationFlowError.INVALID_USER, challengeResponse);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!user.isEnabled()) {
|
if (!user.isEnabled()) {
|
||||||
context.getEvent().user(user);
|
context.getEvent().user(user);
|
||||||
context.getEvent().error(Errors.USER_DISABLED);
|
context.getEvent().error(Errors.USER_DISABLED);
|
||||||
Response challengeResponse = disabledUser(context);
|
Response challengeResponse = disabledUser(context);
|
||||||
context.failureChallenge(AuthenticationProcessor.Error.USER_DISABLED, challengeResponse);
|
context.failureChallenge(AuthenticationFlowError.USER_DISABLED, challengeResponse);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (context.getRealm().isBruteForceProtected()) {
|
if (context.getRealm().isBruteForceProtected()) {
|
||||||
|
@ -113,19 +113,19 @@ public abstract class AbstractFormAuthenticator implements Authenticator {
|
||||||
context.getEvent().user(user);
|
context.getEvent().user(user);
|
||||||
context.getEvent().error(Errors.USER_TEMPORARILY_DISABLED);
|
context.getEvent().error(Errors.USER_TEMPORARILY_DISABLED);
|
||||||
Response challengeResponse = temporarilyDisabledUser(context);
|
Response challengeResponse = temporarilyDisabledUser(context);
|
||||||
context.failureChallenge(AuthenticationProcessor.Error.USER_TEMPORARILY_DISABLED, challengeResponse);
|
context.failureChallenge(AuthenticationFlowError.USER_TEMPORARILY_DISABLED, challengeResponse);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
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);
|
String username = inputData.getFirst(AuthenticationManager.FORM_USERNAME);
|
||||||
if (username == null) {
|
if (username == null) {
|
||||||
context.getEvent().error(Errors.USER_NOT_FOUND);
|
context.getEvent().error(Errors.USER_NOT_FOUND);
|
||||||
Response challengeResponse = invalidUser(context);
|
Response challengeResponse = invalidUser(context);
|
||||||
context.failureChallenge(AuthenticationProcessor.Error.INVALID_USER, challengeResponse);
|
context.failureChallenge(AuthenticationFlowError.INVALID_USER, challengeResponse);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
context.getEvent().detail(Details.USERNAME, username);
|
context.getEvent().detail(Details.USERNAME, username);
|
||||||
|
@ -139,9 +139,9 @@ public abstract class AbstractFormAuthenticator implements Authenticator {
|
||||||
|
|
||||||
// Could happen during federation import
|
// Could happen during federation import
|
||||||
if (mde.getDuplicateFieldName() != null && mde.getDuplicateFieldName().equals(UserModel.EMAIL)) {
|
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 {
|
} 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;
|
return false;
|
||||||
|
@ -160,7 +160,7 @@ public abstract class AbstractFormAuthenticator implements Authenticator {
|
||||||
return true;
|
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<>();
|
List<UserCredentialModel> credentials = new LinkedList<>();
|
||||||
String password = inputData.getFirst(CredentialRepresentation.PASSWORD);
|
String password = inputData.getFirst(CredentialRepresentation.PASSWORD);
|
||||||
if (password == null || password.isEmpty()) {
|
if (password == null || password.isEmpty()) {
|
||||||
|
@ -169,7 +169,7 @@ public abstract class AbstractFormAuthenticator implements Authenticator {
|
||||||
}
|
}
|
||||||
context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
|
context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
|
||||||
Response challengeResponse = invalidCredentials(context);
|
Response challengeResponse = invalidCredentials(context);
|
||||||
context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
|
context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, challengeResponse);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
credentials.add(UserCredentialModel.password(password));
|
credentials.add(UserCredentialModel.password(password));
|
||||||
|
@ -178,7 +178,7 @@ public abstract class AbstractFormAuthenticator implements Authenticator {
|
||||||
context.getEvent().user(context.getUser());
|
context.getEvent().user(context.getUser());
|
||||||
context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
|
context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
|
||||||
Response challengeResponse = invalidCredentials(context);
|
Response challengeResponse = invalidCredentials(context);
|
||||||
context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
|
context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, challengeResponse);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package org.keycloak.authentication.authenticators.browser;
|
package org.keycloak.authentication.authenticators.browser;
|
||||||
|
|
||||||
|
import org.keycloak.authentication.AuthenticationFlowContext;
|
||||||
import org.keycloak.authentication.Authenticator;
|
import org.keycloak.authentication.Authenticator;
|
||||||
import org.keycloak.authentication.AuthenticatorContext;
|
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
@ -19,7 +19,7 @@ public class CookieAuthenticator implements Authenticator {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void authenticate(AuthenticatorContext context) {
|
public void authenticate(AuthenticationFlowContext context) {
|
||||||
AuthenticationManager.AuthResult authResult = AuthenticationManager.authenticateIdentityCookie(context.getSession(),
|
AuthenticationManager.AuthResult authResult = AuthenticationManager.authenticateIdentityCookie(context.getSession(),
|
||||||
context.getRealm(), true);
|
context.getRealm(), true);
|
||||||
if (authResult == null) {
|
if (authResult == null) {
|
||||||
|
@ -33,7 +33,7 @@ public class CookieAuthenticator implements Authenticator {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void action(AuthenticatorContext context) {
|
public void action(AuthenticationFlowContext context) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package org.keycloak.authentication.authenticators.browser;
|
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.Authenticator;
|
||||||
import org.keycloak.authentication.AuthenticatorContext;
|
|
||||||
import org.keycloak.events.Errors;
|
import org.keycloak.events.Errors;
|
||||||
import org.keycloak.login.LoginFormsProvider;
|
import org.keycloak.login.LoginFormsProvider;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
@ -26,17 +26,17 @@ public class OTPFormAuthenticator extends AbstractFormAuthenticator implements A
|
||||||
public static final String TOTP_FORM_ACTION = "totp";
|
public static final String TOTP_FORM_ACTION = "totp";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void action(AuthenticatorContext context) {
|
public void action(AuthenticationFlowContext context) {
|
||||||
validateOTP(context);
|
validateOTP(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void authenticate(AuthenticatorContext context) {
|
public void authenticate(AuthenticationFlowContext context) {
|
||||||
Response challengeResponse = challenge(context, null);
|
Response challengeResponse = challenge(context, null);
|
||||||
context.challenge(challengeResponse);
|
context.challenge(challengeResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void validateOTP(AuthenticatorContext context) {
|
public void validateOTP(AuthenticationFlowContext context) {
|
||||||
MultivaluedMap<String, String> inputData = context.getHttpRequest().getDecodedFormParameters();
|
MultivaluedMap<String, String> inputData = context.getHttpRequest().getDecodedFormParameters();
|
||||||
List<UserCredentialModel> credentials = new LinkedList<>();
|
List<UserCredentialModel> credentials = new LinkedList<>();
|
||||||
String password = inputData.getFirst(CredentialRepresentation.TOTP);
|
String password = inputData.getFirst(CredentialRepresentation.TOTP);
|
||||||
|
@ -51,7 +51,7 @@ public class OTPFormAuthenticator extends AbstractFormAuthenticator implements A
|
||||||
context.getEvent().user(context.getUser())
|
context.getEvent().user(context.getUser())
|
||||||
.error(Errors.INVALID_USER_CREDENTIALS);
|
.error(Errors.INVALID_USER_CREDENTIALS);
|
||||||
Response challengeResponse = challenge(context, Messages.INVALID_TOTP);
|
Response challengeResponse = challenge(context, Messages.INVALID_TOTP);
|
||||||
context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
|
context.failureChallenge(AuthenticationFlowError.INVALID_CREDENTIALS, challengeResponse);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
context.success();
|
context.success();
|
||||||
|
@ -62,7 +62,7 @@ public class OTPFormAuthenticator extends AbstractFormAuthenticator implements A
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Response challenge(AuthenticatorContext context, String error) {
|
protected Response challenge(AuthenticationFlowContext context, String error) {
|
||||||
String accessCode = context.generateAccessCode();
|
String accessCode = context.generateAccessCode();
|
||||||
URI action = getActionUrl(context, accessCode);
|
URI action = getActionUrl(context, accessCode);
|
||||||
LoginFormsProvider forms = context.getSession().getProvider(LoginFormsProvider.class)
|
LoginFormsProvider forms = context.getSession().getProvider(LoginFormsProvider.class)
|
||||||
|
|
|
@ -2,9 +2,9 @@ package org.keycloak.authentication.authenticators.browser;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.jboss.resteasy.spi.HttpRequest;
|
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.Authenticator;
|
||||||
import org.keycloak.authentication.AuthenticatorContext;
|
|
||||||
import org.keycloak.constants.KerberosConstants;
|
import org.keycloak.constants.KerberosConstants;
|
||||||
import org.keycloak.events.Errors;
|
import org.keycloak.events.Errors;
|
||||||
import org.keycloak.login.LoginFormsProvider;
|
import org.keycloak.login.LoginFormsProvider;
|
||||||
|
@ -35,13 +35,13 @@ public class SpnegoAuthenticator extends AbstractFormAuthenticator implements Au
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void action(AuthenticatorContext context) {
|
public void action(AuthenticationFlowContext context) {
|
||||||
context.attempted();
|
context.attempted();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void authenticate(AuthenticatorContext context) {
|
public void authenticate(AuthenticationFlowContext context) {
|
||||||
HttpRequest request = context.getHttpRequest();
|
HttpRequest request = context.getHttpRequest();
|
||||||
String authHeader = request.getHttpHeaders().getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
|
String authHeader = request.getHttpHeaders().getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
|
||||||
if (authHeader == null) {
|
if (authHeader == null) {
|
||||||
|
@ -62,7 +62,7 @@ public class SpnegoAuthenticator extends AbstractFormAuthenticator implements Au
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (tokens.length != 2) {
|
if (tokens.length != 2) {
|
||||||
context.failure(AuthenticationProcessor.Error.INVALID_CREDENTIALS);
|
context.failure(AuthenticationFlowError.INVALID_CREDENTIALS);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,11 +85,11 @@ public class SpnegoAuthenticator extends AbstractFormAuthenticator implements Au
|
||||||
context.challenge(challenge);
|
context.challenge(challenge);
|
||||||
} else {
|
} else {
|
||||||
context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
|
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;
|
String negotiateHeader = negotiateToken == null ? KerberosConstants.NEGOTIATE : KerberosConstants.NEGOTIATE + " " + negotiateToken;
|
||||||
|
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
|
@ -115,7 +115,7 @@ public class SpnegoAuthenticator extends AbstractFormAuthenticator implements Au
|
||||||
* @param negotiateHeader
|
* @param negotiateHeader
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
protected Response optionalChallengeRedirect(AuthenticatorContext context, String negotiateHeader) {
|
protected Response optionalChallengeRedirect(AuthenticationFlowContext context, String negotiateHeader) {
|
||||||
String accessCode = context.generateAccessCode();
|
String accessCode = context.generateAccessCode();
|
||||||
URI action = getActionUrl(context, accessCode);
|
URI action = getActionUrl(context, accessCode);
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,15 @@
|
||||||
package org.keycloak.authentication.authenticators.browser;
|
package org.keycloak.authentication.authenticators.browser;
|
||||||
|
|
||||||
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
|
import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
|
||||||
|
import org.keycloak.authentication.AuthenticationFlowContext;
|
||||||
import org.keycloak.authentication.AuthenticationProcessor;
|
import org.keycloak.authentication.AuthenticationProcessor;
|
||||||
import org.keycloak.authentication.Authenticator;
|
import org.keycloak.authentication.Authenticator;
|
||||||
import org.keycloak.authentication.AuthenticatorContext;
|
|
||||||
import org.keycloak.events.Errors;
|
import org.keycloak.events.Errors;
|
||||||
import org.keycloak.login.LoginFormsProvider;
|
import org.keycloak.login.LoginFormsProvider;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.protocol.LoginProtocol;
|
import org.keycloak.protocol.LoginProtocol;
|
||||||
import org.keycloak.protocol.RestartLoginCookie;
|
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
import org.keycloak.services.managers.AuthenticationManager;
|
import org.keycloak.services.managers.AuthenticationManager;
|
||||||
|
|
||||||
|
@ -24,7 +23,7 @@ import javax.ws.rs.core.Response;
|
||||||
public class UsernamePasswordForm extends AbstractFormAuthenticator implements Authenticator {
|
public class UsernamePasswordForm extends AbstractFormAuthenticator implements Authenticator {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void action(AuthenticatorContext context) {
|
public void action(AuthenticationFlowContext context) {
|
||||||
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
|
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
|
||||||
if (formData.containsKey("cancel")) {
|
if (formData.containsKey("cancel")) {
|
||||||
context.getEvent().error(Errors.REJECTED_BY_USER);
|
context.getEvent().error(Errors.REJECTED_BY_USER);
|
||||||
|
@ -42,12 +41,12 @@ public class UsernamePasswordForm extends AbstractFormAuthenticator implements A
|
||||||
context.success();
|
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);
|
return validateUser(context, formData) && validatePassword(context, formData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void authenticate(AuthenticatorContext context) {
|
public void authenticate(AuthenticationFlowContext context) {
|
||||||
MultivaluedMap<String, String> formData = new MultivaluedMapImpl<>();
|
MultivaluedMap<String, String> formData = new MultivaluedMapImpl<>();
|
||||||
String loginHint = context.getClientSession().getNote(OIDCLoginProtocol.LOGIN_HINT_PARAM);
|
String loginHint = context.getClientSession().getNote(OIDCLoginProtocol.LOGIN_HINT_PARAM);
|
||||||
|
|
||||||
|
@ -71,7 +70,7 @@ public class UsernamePasswordForm extends AbstractFormAuthenticator implements A
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Response challenge(AuthenticatorContext context, MultivaluedMap<String, String> formData) {
|
protected Response challenge(AuthenticationFlowContext context, MultivaluedMap<String, String> formData) {
|
||||||
LoginFormsProvider forms = loginForm(context);
|
LoginFormsProvider forms = loginForm(context);
|
||||||
|
|
||||||
if (formData.size() > 0) forms.setFormData(formData);
|
if (formData.size() > 0) forms.setFormData(formData);
|
||||||
|
|
|
@ -2,8 +2,8 @@ package org.keycloak.authentication.authenticators.directgrant;
|
||||||
|
|
||||||
import org.keycloak.Config;
|
import org.keycloak.Config;
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.OAuth2Constants;
|
||||||
|
import org.keycloak.authentication.AuthenticationFlowContext;
|
||||||
import org.keycloak.authentication.Authenticator;
|
import org.keycloak.authentication.Authenticator;
|
||||||
import org.keycloak.authentication.AuthenticatorContext;
|
|
||||||
import org.keycloak.authentication.AuthenticatorFactory;
|
import org.keycloak.authentication.AuthenticatorFactory;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
|
@ -28,7 +28,7 @@ public abstract class AbstractDirectGrantAuthenticator implements Authenticator,
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void action(AuthenticatorContext context) {
|
public void action(AuthenticationFlowContext context) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package org.keycloak.authentication.authenticators.directgrant;
|
package org.keycloak.authentication.authenticators.directgrant;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.authentication.AuthenticationProcessor;
|
import org.keycloak.authentication.AuthenticationFlowError;
|
||||||
import org.keycloak.authentication.AuthenticatorContext;
|
import org.keycloak.authentication.AuthenticationFlowContext;
|
||||||
import org.keycloak.events.Errors;
|
import org.keycloak.events.Errors;
|
||||||
import org.keycloak.models.AuthenticationExecutionModel;
|
import org.keycloak.models.AuthenticationExecutionModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
@ -27,14 +27,14 @@ public class ValidateOTP extends AbstractDirectGrantAuthenticator {
|
||||||
public static final String PROVIDER_ID = "direct-grant-validate-otp";
|
public static final String PROVIDER_ID = "direct-grant-validate-otp";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void authenticate(AuthenticatorContext context) {
|
public void authenticate(AuthenticationFlowContext context) {
|
||||||
if (!isConfigured(context.getSession(), context.getRealm(), context.getUser())) {
|
if (!isConfigured(context.getSession(), context.getRealm(), context.getUser())) {
|
||||||
if (context.getExecution().isOptional()) {
|
if (context.getExecution().isOptional()) {
|
||||||
context.attempted();
|
context.attempted();
|
||||||
} else if (context.getExecution().isRequired()) {
|
} else if (context.getExecution().isRequired()) {
|
||||||
context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
|
context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
|
||||||
Response challengeResponse = errorResponse(Response.Status.UNAUTHORIZED.getStatusCode(), "invalid_grant", "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;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ public class ValidateOTP extends AbstractDirectGrantAuthenticator {
|
||||||
}
|
}
|
||||||
context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
|
context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
|
||||||
Response challengeResponse = errorResponse(Response.Status.UNAUTHORIZED.getStatusCode(), "invalid_grant", "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;
|
return;
|
||||||
}
|
}
|
||||||
credentials.add(UserCredentialModel.otp(context.getRealm().getOTPPolicy().getType(), otp));
|
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().user(context.getUser());
|
||||||
context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
|
context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
|
||||||
Response challengeResponse = errorResponse(Response.Status.UNAUTHORIZED.getStatusCode(), "invalid_grant", "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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,16 @@
|
||||||
package org.keycloak.authentication.authenticators.directgrant;
|
package org.keycloak.authentication.authenticators.directgrant;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.authentication.AuthenticationProcessor;
|
import org.keycloak.authentication.AuthenticationFlowError;
|
||||||
import org.keycloak.authentication.AuthenticatorContext;
|
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.events.Errors;
|
||||||
import org.keycloak.models.AuthenticationExecutionModel;
|
import org.keycloak.models.AuthenticationExecutionModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.ModelDuplicateException;
|
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserCredentialModel;
|
import org.keycloak.models.UserCredentialModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
|
||||||
import org.keycloak.provider.ProviderConfigProperty;
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||||
import org.keycloak.services.managers.AuthenticationManager;
|
|
||||||
|
|
||||||
import javax.ws.rs.core.MultivaluedMap;
|
import javax.ws.rs.core.MultivaluedMap;
|
||||||
import javax.ws.rs.core.Response;
|
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";
|
public static final String PROVIDER_ID = "direct-grant-validate-password";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void authenticate(AuthenticatorContext context) {
|
public void authenticate(AuthenticationFlowContext context) {
|
||||||
MultivaluedMap<String, String> inputData = context.getHttpRequest().getDecodedFormParameters();
|
MultivaluedMap<String, String> inputData = context.getHttpRequest().getDecodedFormParameters();
|
||||||
List<UserCredentialModel> credentials = new LinkedList<>();
|
List<UserCredentialModel> credentials = new LinkedList<>();
|
||||||
String password = inputData.getFirst(CredentialRepresentation.PASSWORD);
|
String password = inputData.getFirst(CredentialRepresentation.PASSWORD);
|
||||||
|
@ -42,7 +37,7 @@ public class ValidatePassword extends AbstractDirectGrantAuthenticator {
|
||||||
}
|
}
|
||||||
context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
|
context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
|
||||||
Response challengeResponse = errorResponse(Response.Status.UNAUTHORIZED.getStatusCode(), "invalid_grant", "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;
|
return;
|
||||||
}
|
}
|
||||||
credentials.add(UserCredentialModel.password(password));
|
credentials.add(UserCredentialModel.password(password));
|
||||||
|
@ -51,7 +46,7 @@ public class ValidatePassword extends AbstractDirectGrantAuthenticator {
|
||||||
context.getEvent().user(context.getUser());
|
context.getEvent().user(context.getUser());
|
||||||
context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
|
context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
|
||||||
Response challengeResponse = errorResponse(Response.Status.UNAUTHORIZED.getStatusCode(), "invalid_grant", "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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,33 +1,24 @@
|
||||||
package org.keycloak.authentication.authenticators.directgrant;
|
package org.keycloak.authentication.authenticators.directgrant;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.Config;
|
import org.keycloak.authentication.AuthenticationFlowError;
|
||||||
import org.keycloak.OAuth2Constants;
|
import org.keycloak.authentication.AuthenticationFlowContext;
|
||||||
import org.keycloak.authentication.AuthenticationProcessor;
|
|
||||||
import org.keycloak.authentication.Authenticator;
|
|
||||||
import org.keycloak.authentication.AuthenticatorContext;
|
|
||||||
import org.keycloak.authentication.AuthenticatorFactory;
|
|
||||||
import org.keycloak.authentication.authenticators.browser.AbstractFormAuthenticator;
|
import org.keycloak.authentication.authenticators.browser.AbstractFormAuthenticator;
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
import org.keycloak.events.Errors;
|
import org.keycloak.events.Errors;
|
||||||
import org.keycloak.models.AuthenticationExecutionModel;
|
import org.keycloak.models.AuthenticationExecutionModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
|
||||||
import org.keycloak.models.ModelDuplicateException;
|
import org.keycloak.models.ModelDuplicateException;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.provider.ProviderConfigProperty;
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
import org.keycloak.services.managers.AuthenticationManager;
|
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.MultivaluedMap;
|
||||||
import javax.ws.rs.core.Response;
|
import javax.ws.rs.core.Response;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @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";
|
public static final String PROVIDER_ID = "direct-grant-validate-username";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void authenticate(AuthenticatorContext context) {
|
public void authenticate(AuthenticationFlowContext context) {
|
||||||
MultivaluedMap<String, String> inputData = context.getHttpRequest().getDecodedFormParameters();
|
MultivaluedMap<String, String> inputData = context.getHttpRequest().getDecodedFormParameters();
|
||||||
String username = inputData.getFirst(AuthenticationManager.FORM_USERNAME);
|
String username = inputData.getFirst(AuthenticationManager.FORM_USERNAME);
|
||||||
if (username == null) {
|
if (username == null) {
|
||||||
context.getEvent().error(Errors.USER_NOT_FOUND);
|
context.getEvent().error(Errors.USER_NOT_FOUND);
|
||||||
Response challengeResponse = errorResponse(Response.Status.UNAUTHORIZED.getStatusCode(), "invalid_request", "Missing parameter: username");
|
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;
|
return;
|
||||||
}
|
}
|
||||||
context.getEvent().detail(Details.USERNAME, username);
|
context.getEvent().detail(Details.USERNAME, username);
|
||||||
|
@ -57,7 +48,7 @@ public class ValidateUsername extends AbstractDirectGrantAuthenticator {
|
||||||
} catch (ModelDuplicateException mde) {
|
} catch (ModelDuplicateException mde) {
|
||||||
logger.error(mde.getMessage(), mde);
|
logger.error(mde.getMessage(), mde);
|
||||||
Response challengeResponse = errorResponse(Response.Status.UNAUTHORIZED.getStatusCode(), "invalid_request", "Invalid user credentials");
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,14 +56,14 @@ public class ValidateUsername extends AbstractDirectGrantAuthenticator {
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
|
context.getEvent().error(Errors.INVALID_USER_CREDENTIALS);
|
||||||
Response challengeResponse = errorResponse(Response.Status.UNAUTHORIZED.getStatusCode(), "invalid_grant", "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;
|
return;
|
||||||
}
|
}
|
||||||
if (!user.isEnabled()) {
|
if (!user.isEnabled()) {
|
||||||
context.getEvent().user(user);
|
context.getEvent().user(user);
|
||||||
context.getEvent().error(Errors.USER_DISABLED);
|
context.getEvent().error(Errors.USER_DISABLED);
|
||||||
Response challengeResponse = errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "invalid_grant", "Account 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;
|
return;
|
||||||
}
|
}
|
||||||
if (context.getRealm().isBruteForceProtected()) {
|
if (context.getRealm().isBruteForceProtected()) {
|
||||||
|
@ -80,7 +71,7 @@ public class ValidateUsername extends AbstractDirectGrantAuthenticator {
|
||||||
context.getEvent().user(user);
|
context.getEvent().user(user);
|
||||||
context.getEvent().error(Errors.USER_TEMPORARILY_DISABLED);
|
context.getEvent().error(Errors.USER_TEMPORARILY_DISABLED);
|
||||||
Response challengeResponse = errorResponse(Response.Status.BAD_REQUEST.getStatusCode(), "invalid_grant", "Account 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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,7 +88,7 @@ public class TermsAndConditions implements RequiredActionProvider, RequiredActio
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response invokeRequiredAction(RequiredActionContext context) {
|
public Response requiredActionChallenge(RequiredActionContext context) {
|
||||||
return context.getSession().getProvider(LoginFormsProvider.class)
|
return context.getSession().getProvider(LoginFormsProvider.class)
|
||||||
.setClientSessionCode(context.generateAccessCode(getProviderId()))
|
.setClientSessionCode(context.generateAccessCode(getProviderId()))
|
||||||
.setUser(context.getUser())
|
.setUser(context.getUser())
|
||||||
|
|
|
@ -48,7 +48,7 @@ public class UpdatePassword implements RequiredActionProvider, RequiredActionFac
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response invokeRequiredAction(RequiredActionContext context) {
|
public Response requiredActionChallenge(RequiredActionContext context) {
|
||||||
LoginFormsProvider loginFormsProvider = context.getSession()
|
LoginFormsProvider loginFormsProvider = context.getSession()
|
||||||
.getProvider(LoginFormsProvider.class)
|
.getProvider(LoginFormsProvider.class)
|
||||||
.setClientSessionCode(context.generateAccessCode(getProviderId()))
|
.setClientSessionCode(context.generateAccessCode(getProviderId()))
|
||||||
|
|
|
@ -23,7 +23,7 @@ public class UpdateProfile implements RequiredActionProvider, RequiredActionFact
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response invokeRequiredAction(RequiredActionContext context) {
|
public Response requiredActionChallenge(RequiredActionContext context) {
|
||||||
LoginFormsProvider loginFormsProvider = context.getSession().getProvider(LoginFormsProvider.class)
|
LoginFormsProvider loginFormsProvider = context.getSession().getProvider(LoginFormsProvider.class)
|
||||||
.setClientSessionCode(context.generateAccessCode(getProviderId()))
|
.setClientSessionCode(context.generateAccessCode(getProviderId()))
|
||||||
.setUser(context.getUser());
|
.setUser(context.getUser());
|
||||||
|
|
|
@ -25,7 +25,7 @@ public class UpdateTotp implements RequiredActionProvider, RequiredActionFactory
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response invokeRequiredAction(RequiredActionContext context) {
|
public Response requiredActionChallenge(RequiredActionContext context) {
|
||||||
LoginFormsProvider loginFormsProvider = context.getSession().getProvider(LoginFormsProvider.class)
|
LoginFormsProvider loginFormsProvider = context.getSession().getProvider(LoginFormsProvider.class)
|
||||||
.setClientSessionCode(context.generateAccessCode(getProviderId()))
|
.setClientSessionCode(context.generateAccessCode(getProviderId()))
|
||||||
.setUser(context.getUser());
|
.setUser(context.getUser());
|
||||||
|
|
|
@ -34,7 +34,7 @@ public class VerifyEmail implements RequiredActionProvider, RequiredActionFactor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
public Response invokeRequiredAction(RequiredActionContext context) {
|
public Response requiredActionChallenge(RequiredActionContext context) {
|
||||||
if (Validation.isBlank(context.getUser().getEmail())) {
|
if (Validation.isBlank(context.getUser().getEmail())) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,10 @@ import org.keycloak.ClientConnection;
|
||||||
import org.keycloak.RSATokenVerifier;
|
import org.keycloak.RSATokenVerifier;
|
||||||
import org.keycloak.VerificationException;
|
import org.keycloak.VerificationException;
|
||||||
import org.keycloak.authentication.RequiredActionContext;
|
import org.keycloak.authentication.RequiredActionContext;
|
||||||
import org.keycloak.authentication.RequiredActionFactory;
|
|
||||||
import org.keycloak.authentication.RequiredActionProvider;
|
import org.keycloak.authentication.RequiredActionProvider;
|
||||||
import org.keycloak.broker.provider.IdentityProvider;
|
import org.keycloak.broker.provider.IdentityProvider;
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
import org.keycloak.events.EventBuilder;
|
import org.keycloak.events.EventBuilder;
|
||||||
import org.keycloak.events.EventType;
|
|
||||||
import org.keycloak.jose.jws.JWSBuilder;
|
import org.keycloak.jose.jws.JWSBuilder;
|
||||||
import org.keycloak.login.LoginFormsProvider;
|
import org.keycloak.login.LoginFormsProvider;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
|
@ -22,26 +20,18 @@ import org.keycloak.models.UserConsentModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.ProtocolMapperModel;
|
import org.keycloak.models.ProtocolMapperModel;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RequiredCredentialModel;
|
|
||||||
import org.keycloak.models.RoleModel;
|
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;
|
||||||
import org.keycloak.models.UserModel.RequiredAction;
|
|
||||||
import org.keycloak.models.UserSessionModel;
|
import org.keycloak.models.UserSessionModel;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
import org.keycloak.protocol.LoginProtocol;
|
import org.keycloak.protocol.LoginProtocol;
|
||||||
import org.keycloak.protocol.RestartLoginCookie;
|
import org.keycloak.protocol.RestartLoginCookie;
|
||||||
import org.keycloak.protocol.oidc.TokenManager;
|
import org.keycloak.protocol.oidc.TokenManager;
|
||||||
import org.keycloak.provider.ProviderFactory;
|
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
|
||||||
import org.keycloak.services.resources.IdentityBrokerService;
|
import org.keycloak.services.resources.IdentityBrokerService;
|
||||||
import org.keycloak.services.resources.LoginActionsService;
|
|
||||||
import org.keycloak.services.resources.RealmsResource;
|
import org.keycloak.services.resources.RealmsResource;
|
||||||
import org.keycloak.services.Urls;
|
import org.keycloak.services.Urls;
|
||||||
import org.keycloak.services.util.CookieHelper;
|
import org.keycloak.services.util.CookieHelper;
|
||||||
import org.keycloak.services.validation.Validation;
|
|
||||||
import org.keycloak.util.Time;
|
import org.keycloak.util.Time;
|
||||||
|
|
||||||
import javax.ws.rs.core.Cookie;
|
import javax.ws.rs.core.Cookie;
|
||||||
|
@ -52,12 +42,9 @@ import javax.ws.rs.core.Response;
|
||||||
import javax.ws.rs.core.UriInfo;
|
import javax.ws.rs.core.UriInfo;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stateless object that manages authentication
|
* Stateless object that manages authentication
|
||||||
|
@ -442,7 +429,7 @@ public class AuthenticationManager {
|
||||||
for (String action : requiredActions) {
|
for (String action : requiredActions) {
|
||||||
RequiredActionProviderModel model = realm.getRequiredActionProviderByAlias(action);
|
RequiredActionProviderModel model = realm.getRequiredActionProviderByAlias(action);
|
||||||
RequiredActionProvider actionProvider = session.getProvider(RequiredActionProvider.class, model.getProviderId());
|
RequiredActionProvider actionProvider = session.getProvider(RequiredActionProvider.class, model.getProviderId());
|
||||||
Response challenge = actionProvider.invokeRequiredAction(context);
|
Response challenge = actionProvider.requiredActionChallenge(context);
|
||||||
if (challenge != null) {
|
if (challenge != null) {
|
||||||
return challenge;
|
return challenge;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package org.keycloak.testsuite.forms;
|
package org.keycloak.testsuite.forms;
|
||||||
|
|
||||||
import org.keycloak.Config;
|
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.Authenticator;
|
||||||
import org.keycloak.authentication.AuthenticatorContext;
|
|
||||||
import org.keycloak.authentication.AuthenticatorFactory;
|
import org.keycloak.authentication.AuthenticatorFactory;
|
||||||
import org.keycloak.models.AuthenticationExecutionModel;
|
import org.keycloak.models.AuthenticationExecutionModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
@ -24,10 +24,10 @@ public class PassThroughAuthenticator implements Authenticator, AuthenticatorFac
|
||||||
public static String username = "test-user@localhost";
|
public static String username = "test-user@localhost";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void authenticate(AuthenticatorContext context) {
|
public void authenticate(AuthenticationFlowContext context) {
|
||||||
UserModel user = KeycloakModelUtils.findUserByNameOrEmail(context.getSession(), context.getRealm(), username);
|
UserModel user = KeycloakModelUtils.findUserByNameOrEmail(context.getSession(), context.getRealm(), username);
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
context.failure(AuthenticationProcessor.Error.UNKNOWN_USER);
|
context.failure(AuthenticationFlowError.UNKNOWN_USER);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
context.setUser(user);
|
context.setUser(user);
|
||||||
|
@ -50,7 +50,7 @@ public class PassThroughAuthenticator implements Authenticator, AuthenticatorFac
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void action(AuthenticatorContext context) {
|
public void action(AuthenticationFlowContext context) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
package org.keycloak.testsuite.forms;
|
package org.keycloak.testsuite.forms;
|
||||||
|
|
||||||
import org.keycloak.Config;
|
import org.keycloak.Config;
|
||||||
import org.keycloak.authentication.AuthenticationProcessor;
|
import org.keycloak.authentication.AuthenticationFlowContext;
|
||||||
import org.keycloak.authentication.Authenticator;
|
import org.keycloak.authentication.Authenticator;
|
||||||
import org.keycloak.authentication.AuthenticatorContext;
|
|
||||||
import org.keycloak.authentication.AuthenticatorFactory;
|
import org.keycloak.authentication.AuthenticatorFactory;
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
import org.keycloak.events.EventType;
|
import org.keycloak.events.EventType;
|
||||||
|
@ -12,10 +11,8 @@ import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.utils.KeycloakModelUtils;
|
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
import org.keycloak.provider.ProviderConfigProperty;
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
import org.keycloak.services.resources.AttributeFormDataProcessor;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -29,7 +26,7 @@ public class PassThroughRegistration implements Authenticator, AuthenticatorFact
|
||||||
public static String email = "new-user@localhost";
|
public static String email = "new-user@localhost";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void authenticate(AuthenticatorContext context) {
|
public void authenticate(AuthenticationFlowContext context) {
|
||||||
context.getEvent().detail(Details.USERNAME, username)
|
context.getEvent().detail(Details.USERNAME, username)
|
||||||
.detail(Details.REGISTER_METHOD, "form")
|
.detail(Details.REGISTER_METHOD, "form")
|
||||||
.detail(Details.EMAIL, email)
|
.detail(Details.EMAIL, email)
|
||||||
|
@ -69,7 +66,7 @@ public class PassThroughRegistration implements Authenticator, AuthenticatorFact
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void action(AuthenticatorContext context) {
|
public void action(AuthenticationFlowContext context) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue