finalSelectionOptions = selectionOptions.stream().filter(aso -> !aso.getAuthenticationExecution().isAuthenticatorFlow() && !isProcessed(aso.getAuthenticationExecution())).collect(Collectors.toList());;
if (finalSelectionOptions.isEmpty()) {
//move to next
return null;
}
model = finalSelectionOptions.get(0).getAuthenticationExecution();
factory = (AuthenticatorFactory) processor.getSession().getKeycloakSessionFactory().getProviderFactory(Authenticator.class, model.getAuthenticator());
if (factory == null) {
throw new RuntimeException("Unable to find factory for AuthenticatorFactory: " + model.getAuthenticator() + " did you forget to declare it in a META-INF/services file?");
}
authenticator = createAuthenticator(factory);
}
AuthenticationProcessor.Result context = processor.createAuthenticatorContext(model, authenticator, executions);
context.setAuthenticationSelections(selectionOptions);
if (selectedCredentialId != null) {
context.setSelectedCredentialId(selectedCredentialId);
} else if (!selectionOptions.isEmpty()) {
context.setSelectedCredentialId(selectionOptions.get(0).getCredentialId());
}
if (authenticator.requiresUser()) {
if (authUser == null) {
throw new AuthenticationFlowException("authenticator: " + factory.getId(), AuthenticationFlowError.UNKNOWN_USER);
}
if (!authenticator.configuredFor(processor.getSession(), processor.getRealm(), authUser)) {
if (factory.isUserSetupAllowed() && model.isRequired() && authenticator.areRequiredActionsEnabled(processor.getSession(), processor.getRealm())) {
//This means that having even though the user didn't validate the
logger.debugv("authenticator SETUP_REQUIRED: {0}", factory.getId());
processor.getAuthenticationSession().setExecutionStatus(model.getId(), AuthenticationSessionModel.ExecutionStatus.SETUP_REQUIRED);
authenticator.setRequiredActions(processor.getSession(), processor.getRealm(), processor.getAuthenticationSession().getAuthenticatedUser());
return null;
} else {
throw new AuthenticationFlowException("authenticator: " + factory.getId(), AuthenticationFlowError.CREDENTIAL_SETUP_REQUIRED);
}
}
}
logger.debugv("invoke authenticator.authenticate: {0}", factory.getId());
authenticator.authenticate(context);
return processResult(context, false);
}
/**
* This method creates the list of authenticators that is presented to the user. For a required execution, this is
* only the credentials associated to the authenticator, and for an alternative execution, this is all other alternative
* executions in the flow, including the credentials.
*
* In both cases, the credentials take precedence, with the order selected by the user (or his administrator).
*
* @param model The current execution model
* @return an ordered list of the authentication selection options to present the user.
*/
private List createAuthenticationSelectionList(AuthenticationExecutionModel model) {
List authenticationSelectionList = new ArrayList<>();
if (processor.getAuthenticationSession() != null) {
Map typeAuthExecMap = new HashMap<>();
List nonCredentialExecutions = new ArrayList<>();
if (model.isAlternative()) {
//get all alternative executions to be able to list their credentials
List alternativeExecutions = processor.getRealm().getAuthenticationExecutions(model.getParentFlow())
.stream().filter(AuthenticationExecutionModel::isAlternative).collect(Collectors.toList());
for (AuthenticationExecutionModel execution : alternativeExecutions) {
if (!execution.isAuthenticatorFlow()) {
Authenticator localAuthenticator = processor.getSession().getProvider(Authenticator.class, execution.getAuthenticator());
if (!(localAuthenticator instanceof CredentialValidator)) {
nonCredentialExecutions.add(execution);
continue;
}
CredentialValidator> cv = (CredentialValidator>) localAuthenticator;
typeAuthExecMap.put(cv.getType(processor.getSession()), execution);
} else {
nonCredentialExecutions.add(execution);
}
}
} else if (model.isRequired() && !model.isAuthenticatorFlow()) {
//only get current credentials
Authenticator authenticator = processor.getSession().getProvider(Authenticator.class, model.getAuthenticator());
if (authenticator instanceof CredentialValidator) {
typeAuthExecMap.put(((CredentialValidator>) authenticator).getType(processor.getSession()), model);
}
}
//add credential authenticators in order
if (processor.getAuthenticationSession().getAuthenticatedUser() != null) {
List credentials = processor.getSession().userCredentialManager()
.getStoredCredentials(processor.getRealm(), processor.getAuthenticationSession().getAuthenticatedUser())
.stream()
.filter(credential -> typeAuthExecMap.containsKey(credential.getType()))
.collect(Collectors.toList());
MultivaluedMap countAuthSelections = new MultivaluedHashMap<>();
for (CredentialModel credential : credentials) {
AuthenticationSelectionOption authSel = new AuthenticationSelectionOption(typeAuthExecMap.get(credential.getType()), credential);
authenticationSelectionList.add(authSel);
countAuthSelections.add(credential.getType(), authSel);
}
for (Entry> entry : countAuthSelections.entrySet()) {
if (entry.getValue().size() == 1) {
entry.getValue().get(0).setShowCredentialName(false);
}
}
//don't show credential type if there's only a single type in the list
if (countAuthSelections.keySet().size() == 1 && nonCredentialExecutions.isEmpty()) {
for (AuthenticationSelectionOption so : authenticationSelectionList) {
so.setShowCredentialType(false);
}
}
}
//add all other authenticators (including flows)
for (AuthenticationExecutionModel exec : nonCredentialExecutions) {
if (exec.isAuthenticatorFlow()) {
authenticationSelectionList.add(new AuthenticationSelectionOption(exec,
processor.getRealm().getAuthenticationFlowById(exec.getFlowId())));
} else {
authenticationSelectionList.add(new AuthenticationSelectionOption(exec));
}
}
}
return authenticationSelectionList;
}
public Response processResult(AuthenticationProcessor.Result result, boolean isAction) {
AuthenticationExecutionModel execution = result.getExecution();
FlowStatus status = result.getStatus();
switch (status) {
case SUCCESS:
logger.debugv("authenticator SUCCESS: {0}", execution.getAuthenticator());
if (isAction) {
new AuthenticationFlowHistoryHelper(processor).pushExecution(execution.getId());
}
processor.getAuthenticationSession().setExecutionStatus(execution.getId(), AuthenticationSessionModel.ExecutionStatus.SUCCESS);
return null;
case FAILED:
logger.debugv("authenticator FAILED: {0}", execution.getAuthenticator());
processor.logFailure();
processor.getAuthenticationSession().setExecutionStatus(execution.getId(), AuthenticationSessionModel.ExecutionStatus.FAILED);
if (result.getChallenge() != null) {
return sendChallenge(result, execution);
}
throw new AuthenticationFlowException(result.getError());
case FORK:
logger.debugv("reset browser login from authenticator: {0}", execution.getAuthenticator());
processor.getAuthenticationSession().setAuthNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, execution.getId());
throw new ForkFlowException(result.getSuccessMessage(), result.getErrorMessage());
case FORCE_CHALLENGE:
case CHALLENGE:
processor.getAuthenticationSession().setExecutionStatus(execution.getId(), AuthenticationSessionModel.ExecutionStatus.CHALLENGED);
return sendChallenge(result, execution);
case FAILURE_CHALLENGE:
logger.debugv("authenticator FAILURE_CHALLENGE: {0}", execution.getAuthenticator());
processor.logFailure();
processor.getAuthenticationSession().setExecutionStatus(execution.getId(), AuthenticationSessionModel.ExecutionStatus.CHALLENGED);
return sendChallenge(result, execution);
case ATTEMPTED:
logger.debugv("authenticator ATTEMPTED: {0}", execution.getAuthenticator());
if (execution.isRequired()) {
throw new AuthenticationFlowException(AuthenticationFlowError.INVALID_CREDENTIALS);
}
processor.getAuthenticationSession().setExecutionStatus(execution.getId(), AuthenticationSessionModel.ExecutionStatus.ATTEMPTED);
return null;
case FLOW_RESET:
processor.resetFlow();
return processor.authenticate();
default:
logger.debugv("authenticator INTERNAL_ERROR: {0}", execution.getAuthenticator());
ServicesLogger.LOGGER.unknownResultStatus();
throw new AuthenticationFlowException(AuthenticationFlowError.INTERNAL_ERROR);
}
}
public Response sendChallenge(AuthenticationProcessor.Result result, AuthenticationExecutionModel execution) {
processor.getAuthenticationSession().setAuthNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, execution.getId());
return result.getChallenge();
}
@Override
public boolean isSuccessful() {
return successful;
}
@Override
public List getFlowExceptions(){
return afeList;
}
}