use of org.keycloak.models.AuthenticationExecutionModel in project keycloak by keycloak.
the class FormAuthenticationFlow method renderForm.
public Response renderForm(MultivaluedMap<String, String> formData, List<FormMessage> errors) {
String executionId = formExecution.getId();
processor.getAuthenticationSession().setAuthNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, executionId);
String code = processor.generateCode();
URI actionUrl = getActionUrl(executionId, code);
LoginFormsProvider form = processor.getSession().getProvider(LoginFormsProvider.class).setAuthenticationSession(processor.getAuthenticationSession()).setActionUri(actionUrl).setExecution(executionId).setClientSessionCode(code).setFormData(formData).setErrors(errors);
for (AuthenticationExecutionModel formActionExecution : formActionExecutions) {
if (!formActionExecution.isEnabled())
continue;
FormAction action = processor.getSession().getProvider(FormAction.class, formActionExecution.getAuthenticator());
FormContext result = new FormContextImpl(formActionExecution);
action.buildPage(result, form);
}
FormContext context = new FormContextImpl(formExecution);
return formAuthenticator.render(context, form);
}
use of org.keycloak.models.AuthenticationExecutionModel in project keycloak by keycloak.
the class FormAuthenticationFlow method processAction.
@Override
public Response processAction(String actionExecution) {
if (!actionExecution.equals(formExecution.getId())) {
throw new AuthenticationFlowException("action is not current execution", AuthenticationFlowError.INTERNAL_ERROR);
}
Map<String, AuthenticationSessionModel.ExecutionStatus> executionStatus = new HashMap<>();
List<FormAction> requiredActions = new LinkedList<>();
List<ValidationContextImpl> successes = new LinkedList<>();
List<ValidationContextImpl> errors = new LinkedList<>();
for (AuthenticationExecutionModel formActionExecution : formActionExecutions) {
if (!formActionExecution.isEnabled()) {
executionStatus.put(formActionExecution.getId(), AuthenticationSessionModel.ExecutionStatus.SKIPPED);
continue;
}
FormActionFactory factory = (FormActionFactory) processor.getSession().getKeycloakSessionFactory().getProviderFactory(FormAction.class, formActionExecution.getAuthenticator());
FormAction action = factory.create(processor.getSession());
UserModel authUser = processor.getAuthenticationSession().getAuthenticatedUser();
if (action.requiresUser() && authUser == null) {
throw new AuthenticationFlowException("form action: " + formExecution.getAuthenticator() + " requires user", AuthenticationFlowError.UNKNOWN_USER);
}
boolean configuredFor = false;
if (action.requiresUser() && authUser != null) {
configuredFor = action.configuredFor(processor.getSession(), processor.getRealm(), authUser);
if (!configuredFor) {
if (formActionExecution.isRequired()) {
if (factory.isUserSetupAllowed()) {
AuthenticationProcessor.logger.debugv("authenticator SETUP_REQUIRED: {0}", formExecution.getAuthenticator());
executionStatus.put(formActionExecution.getId(), AuthenticationSessionModel.ExecutionStatus.SETUP_REQUIRED);
requiredActions.add(action);
continue;
} else {
throw new AuthenticationFlowException(AuthenticationFlowError.CREDENTIAL_SETUP_REQUIRED);
}
} else if (formActionExecution.isConditional()) {
executionStatus.put(formActionExecution.getId(), AuthenticationSessionModel.ExecutionStatus.SKIPPED);
continue;
}
}
}
ValidationContextImpl result = new ValidationContextImpl(formActionExecution, action);
action.validate(result);
if (result.success) {
executionStatus.put(formActionExecution.getId(), AuthenticationSessionModel.ExecutionStatus.SUCCESS);
successes.add(result);
} else {
executionStatus.put(formActionExecution.getId(), AuthenticationSessionModel.ExecutionStatus.CHALLENGED);
errors.add(result);
}
}
if (!errors.isEmpty()) {
processor.logFailure();
List<FormMessage> messages = new LinkedList<>();
Set<String> fields = new HashSet<>();
for (ValidationContextImpl v : errors) {
for (FormMessage m : v.errors) {
if (!fields.contains(m.getField())) {
if (v.excludeOthers) {
fields.clear();
messages.clear();
}
fields.add(m.getField());
messages.add(m);
if (v.excludeOthers) {
break;
}
}
}
}
ValidationContextImpl first = errors.get(0);
first.getEvent().error(first.error);
return renderForm(first.formData, messages);
}
for (ValidationContextImpl context : successes) {
context.action.success(context);
}
// set status and required actions only if form is fully successful
for (Map.Entry<String, AuthenticationSessionModel.ExecutionStatus> entry : executionStatus.entrySet()) {
processor.getAuthenticationSession().setExecutionStatus(entry.getKey(), entry.getValue());
}
for (FormAction action : requiredActions) {
action.setRequiredActions(processor.getSession(), processor.getRealm(), processor.getAuthenticationSession().getAuthenticatedUser());
}
processor.getAuthenticationSession().setExecutionStatus(actionExecution, AuthenticationSessionModel.ExecutionStatus.SUCCESS);
processor.getAuthenticationSession().removeAuthNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION);
return null;
}
use of org.keycloak.models.AuthenticationExecutionModel in project keycloak by keycloak.
the class AuthenticationSelectionResolver method createAuthenticationSelectionList.
/**
* 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.
* <p>
* In both cases, the credentials take precedence, with the order selected by the user (or his administrator).
* <p>
* The implementation needs to take various subflows into account.
*
* For example during configuration of the authentication flow like this:
* - WebAuthn: ALTERNATIVE
* - Password-and-OTP subflow: ALTERNATIVE
* - Password REQUIRED
* - OTP REQUIRED
* The user can authenticate with: WebAuthn OR (Password AND OTP). In this case, the user should be able to choose between WebAuthn and Password
* even if those mechanisms are in different subflows
*
* @param model The current execution model
* @return an ordered list of the authentication selection options to present the user.
*/
static List<AuthenticationSelectionOption> createAuthenticationSelectionList(AuthenticationProcessor processor, AuthenticationExecutionModel model) {
List<AuthenticationSelectionOption> authenticationSelectionList = new ArrayList<>();
if (processor.getAuthenticationSession() != null) {
Map<String, AuthenticationExecutionModel> typeAuthExecMap = new HashMap<>();
List<AuthenticationExecutionModel> nonCredentialExecutions = new ArrayList<>();
String topFlowId = getFlowIdOfTheHighestUsefulFlow(processor, model);
if (topFlowId == null) {
addSimpleAuthenticationExecution(processor, model, typeAuthExecMap, nonCredentialExecutions);
} else {
addAllExecutionsFromSubflow(processor, topFlowId, typeAuthExecMap, nonCredentialExecutions);
}
// add credential authenticators in order
if (processor.getAuthenticationSession().getAuthenticatedUser() != null) {
authenticationSelectionList = Stream.concat(processor.getSession().userCredentialManager().getStoredCredentialsStream(processor.getRealm(), processor.getAuthenticationSession().getAuthenticatedUser()).map(CredentialModel::getType), processor.getSession().userCredentialManager().getConfiguredUserStorageCredentialTypesStream(processor.getRealm(), processor.getAuthenticationSession().getAuthenticatedUser())).distinct().filter(typeAuthExecMap::containsKey).map(credentialType -> new AuthenticationSelectionOption(processor.getSession(), typeAuthExecMap.get(credentialType))).collect(Collectors.toList());
}
// add all other authenticators
for (AuthenticationExecutionModel exec : nonCredentialExecutions) {
authenticationSelectionList.add(new AuthenticationSelectionOption(processor.getSession(), exec));
}
}
logger.debugf("Selections when trying execution '%s' : %s", model.getAuthenticator(), authenticationSelectionList);
return authenticationSelectionList;
}
use of org.keycloak.models.AuthenticationExecutionModel in project keycloak by keycloak.
the class AuthenticationSelectionResolver method addAllExecutionsFromSubflow.
/**
* Fill the typeAuthExecMap and nonCredentialExecutions collections with all available authentication mechanisms for the particular subflow with
* given flowId
*
* Return true if at least something was added to any of the list
*/
private static boolean addAllExecutionsFromSubflow(AuthenticationProcessor processor, String flowId, Map<String, AuthenticationExecutionModel> typeAuthExecMap, List<AuthenticationExecutionModel> nonCredentialExecutions) {
AuthenticationFlowModel flowModel = processor.getRealm().getAuthenticationFlowById(flowId);
if (flowModel == null) {
throw new AuthenticationFlowException("Flow not found", AuthenticationFlowError.INTERNAL_ERROR);
}
DefaultAuthenticationFlow flow = new DefaultAuthenticationFlow(processor, flowModel);
logger.debugf("Going through the flow '%s' for adding executions", flowModel.getAlias());
List<AuthenticationExecutionModel> requiredList = new ArrayList<>();
List<AuthenticationExecutionModel> alternativeList = new ArrayList<>();
flow.fillListsOfExecutions(processor.getRealm().getAuthenticationExecutionsStream(flowId), requiredList, alternativeList);
// If requiredList is not empty, we're going to collect just very first execution from the flow
if (!requiredList.isEmpty()) {
AuthenticationExecutionModel requiredExecution = requiredList.stream().filter(ex -> {
if (ex.isRequired())
return true;
// requiredExecution in the list
return !flow.isConditionalSubflowDisabled(ex);
}).findFirst().orElse(null);
// Not requiredExecution found. Returning false as we did not add any authenticator
if (requiredExecution == null)
return false;
// Don't add already processed executions
if (flow.isProcessed(requiredExecution)) {
return false;
}
FormAuthenticatorFactory factory = (FormAuthenticatorFactory) processor.getSession().getKeycloakSessionFactory().getProviderFactory(FormAuthenticator.class, requiredExecution.getAuthenticator());
// Recursively add credentials from required execution
if (requiredExecution.isAuthenticatorFlow() && factory == null) {
return addAllExecutionsFromSubflow(processor, requiredExecution.getFlowId(), typeAuthExecMap, nonCredentialExecutions);
} else {
addSimpleAuthenticationExecution(processor, requiredExecution, typeAuthExecMap, nonCredentialExecutions);
return true;
}
} else {
// We're going through all the alternatives
boolean anyAdded = false;
for (AuthenticationExecutionModel execution : alternativeList) {
// Don't add already processed executions
if (flow.isProcessed(execution)) {
continue;
}
if (!execution.isAuthenticatorFlow()) {
addSimpleAuthenticationExecution(processor, execution, typeAuthExecMap, nonCredentialExecutions);
anyAdded = true;
} else {
anyAdded |= addAllExecutionsFromSubflow(processor, execution.getFlowId(), typeAuthExecMap, nonCredentialExecutions);
}
}
return anyAdded;
}
}
use of org.keycloak.models.AuthenticationExecutionModel in project keycloak by keycloak.
the class ClientAuthenticationFlow method processResult.
protected Response processResult(AuthenticationProcessor.Result result) {
AuthenticationExecutionModel execution = result.getExecution();
FlowStatus status = result.getStatus();
logger.debugv("client authenticator {0}: {1}", status.toString(), execution.getAuthenticator());
if (status == FlowStatus.SUCCESS) {
return null;
}
if (status == FlowStatus.FAILED) {
if (result.getChallenge() != null) {
return sendChallenge(result, execution);
} else {
throw new AuthenticationFlowException(result.getError());
}
} else if (status == FlowStatus.FORCE_CHALLENGE) {
return sendChallenge(result, execution);
} else if (status == FlowStatus.CHALLENGE) {
// Make sure the first priority alternative challenge is used
if (alternativeChallenge == null) {
alternativeChallenge = result.getChallenge();
}
return sendChallenge(result, execution);
} else if (status == FlowStatus.FAILURE_CHALLENGE) {
return sendChallenge(result, execution);
} else {
ServicesLogger.LOGGER.unknownResultStatus();
throw new AuthenticationFlowException(AuthenticationFlowError.INTERNAL_ERROR);
}
}
Aggregations