registration flow

This commit is contained in:
Bill Burke 2015-06-29 22:12:06 -04:00
parent be6c843bba
commit afa65d9ead
30 changed files with 922 additions and 120 deletions

View file

@ -56,6 +56,9 @@ public class UrlBean {
}
public String getRegistrationAction() {
if (this.actionuri != null) {
return this.actionuri.toString();
}
return Urls.realmRegisterAction(baseURI, realm).toString();
}

View file

@ -2,7 +2,6 @@ package org.keycloak.models.utils;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.AuthenticatorConfigModel;
import org.keycloak.models.RealmModel;
/**
@ -11,10 +10,80 @@ import org.keycloak.models.RealmModel;
*/
public class DefaultAuthenticationFlows {
public static final String REGISTRATION_FLOW = "registration";
public static final String REGISTRATION_FORM_FLOW = "registration form";
public static final String BROWSER_FLOW = "browser";
public static final String FORMS_FLOW = "forms";
public static final String LOGIN_FORMS_FLOW = "forms";
public static void addFlows(RealmModel realm) {
browserFlow(realm);
registrationFlow(realm);
}
public static void registrationFlow(RealmModel realm) {
AuthenticationFlowModel registrationFlow = new AuthenticationFlowModel();
registrationFlow.setAlias(REGISTRATION_FLOW);
registrationFlow.setDescription("registration flow");
registrationFlow.setProviderId("basic-flow");
registrationFlow = realm.addAuthenticationFlow(registrationFlow);
AuthenticationFlowModel registrationFormFlow = new AuthenticationFlowModel();
registrationFormFlow.setAlias(REGISTRATION_FORM_FLOW);
registrationFormFlow.setDescription("registration form");
registrationFormFlow.setProviderId("form-flow");
registrationFormFlow = realm.addAuthenticationFlow(registrationFormFlow);
AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
execution.setParentFlow(registrationFlow.getId());
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
execution.setAuthenticator("registration-page-form");
execution.setPriority(10);
execution.setUserSetupAllowed(false);
execution.setAutheticatorFlow(true);
execution.setFlowId(registrationFormFlow.getId());
realm.addAuthenticatorExecution(execution);
execution = new AuthenticationExecutionModel();
execution.setParentFlow(registrationFormFlow.getId());
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
execution.setAuthenticator("username-validation-action");
execution.setPriority(20);
execution.setUserSetupAllowed(false);
execution.setAutheticatorFlow(false);
realm.addAuthenticatorExecution(execution);
execution = new AuthenticationExecutionModel();
execution.setParentFlow(registrationFormFlow.getId());
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
execution.setAuthenticator("profile-validation-action");
execution.setPriority(30);
execution.setUserSetupAllowed(false);
execution.setAutheticatorFlow(false);
realm.addAuthenticatorExecution(execution);
execution = new AuthenticationExecutionModel();
execution.setParentFlow(registrationFormFlow.getId());
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
execution.setAuthenticator("password-validation-action");
execution.setPriority(40);
execution.setUserSetupAllowed(false);
execution.setAutheticatorFlow(false);
realm.addAuthenticatorExecution(execution);
execution = new AuthenticationExecutionModel();
execution.setParentFlow(registrationFormFlow.getId());
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
execution.setAuthenticator("registration-user-creation");
execution.setPriority(50);
execution.setUserSetupAllowed(false);
execution.setAutheticatorFlow(false);
realm.addAuthenticatorExecution(execution);
}
public static void browserFlow(RealmModel realm) {
AuthenticationFlowModel browser = new AuthenticationFlowModel();
browser.setAlias(BROWSER_FLOW);
browser.setDescription("browser based authentication");
@ -39,7 +108,7 @@ public class DefaultAuthenticationFlows {
AuthenticationFlowModel forms = new AuthenticationFlowModel();
forms.setAlias(FORMS_FLOW);
forms.setAlias(LOGIN_FORMS_FLOW);
forms.setDescription("Username, password, otp and other auth forms.");
forms.setProviderId("basic-flow");
forms = realm.addAuthenticationFlow(forms);
@ -72,8 +141,5 @@ public class DefaultAuthenticationFlows {
execution.setUserSetupAllowed(true);
execution.setAutheticatorFlow(false);
realm.addAuthenticatorExecution(execution);
//
}
}

View file

@ -26,6 +26,7 @@ import org.keycloak.util.Time;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -167,10 +168,25 @@ public class AuthenticationProcessor {
Status status;
Response challenge;
Error error;
List<AuthenticationExecutionModel> currentExecutions;
private Result(AuthenticationExecutionModel execution, Authenticator authenticator) {
private Result(AuthenticationExecutionModel execution, Authenticator authenticator, List<AuthenticationExecutionModel> currentExecutions) {
this.execution = execution;
this.authenticator = authenticator;
this.currentExecutions = currentExecutions;
}
@Override
public AuthenticationExecutionModel.Requirement getCategoryRequirementFromCurrentFlow(String authenticatorCategory) {
List<AuthenticationExecutionModel> executions = realm.getAuthenticationExecutions(execution.getParentFlow());
for (AuthenticationExecutionModel exe : executions) {
AuthenticatorFactory factory = (AuthenticatorFactory) getSession().getKeycloakSessionFactory().getProviderFactory(Authenticator.class, exe.getAuthenticator());
if (factory != null && factory.getReferenceCategory().equals(authenticatorCategory)) {
return exe.getRequirement();
}
}
return null;
}
@Override
@ -434,8 +450,7 @@ public class AuthenticationProcessor {
throw new AuthException(Error.INTERNAL_ERROR);
}
if (flow.getProviderId() == null || flow.getProviderId().equals("basic-flow")) {
DefaultAuthenticationFlow flowExecution = new DefaultAuthenticationFlow(this);
flowExecution.executions = realm.getAuthenticationExecutions(flow.getId()).iterator();
DefaultAuthenticationFlow flowExecution = new DefaultAuthenticationFlow(this, flow);
return flowExecution;
} else if (flow.getProviderId().equals("form-flow")) {
@ -587,8 +602,8 @@ public class AuthenticationProcessor {
}
public AuthenticatorContext createAuthenticatorContext(AuthenticationExecutionModel model, Authenticator authenticator) {
return new Result(model, authenticator);
public AuthenticatorContext createAuthenticatorContext(AuthenticationExecutionModel model, Authenticator authenticator, List<AuthenticationExecutionModel> executions) {
return new Result(model, authenticator, executions);
}

View file

@ -54,6 +54,8 @@ public interface AuthenticatorContext {
HttpRequest getHttpRequest();
BruteForceProtector getProtector();
AuthenticationExecutionModel.Requirement getCategoryRequirementFromCurrentFlow(String authenticatorCategory);
void success();
void failure(AuthenticationProcessor.Error error);
void failure(AuthenticationProcessor.Error error, Response response);

View file

@ -9,24 +9,7 @@ import org.keycloak.provider.ProviderFactory;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface AuthenticatorFactory extends ProviderFactory<Authenticator>, ConfiguredProvider {
public interface AuthenticatorFactory extends ProviderFactory<Authenticator>, ConfiguredProvider, ConfigurableAuthenticatorFactory {
Authenticator create();
String getDisplayType();
/**
* General authenticator type, i.e. totp, password, cert.
*
* @return null if not a referencable type
*/
String getReferenceType();
boolean isConfigurable();
/**
* What requirement settings are allowed.
*
* @return
*/
AuthenticationExecutionModel.Requirement[] getRequirementChoices();
}

View file

@ -0,0 +1,27 @@
package org.keycloak.authentication;
import org.keycloak.models.AuthenticationExecutionModel;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface ConfigurableAuthenticatorFactory {
String getDisplayType();
/**
* General authenticator type, i.e. totp, password, cert.
*
* @return null if not a referencable category
*/
String getReferenceCategory();
boolean isConfigurable();
/**
* What requirement settings are allowed.
*
* @return
*/
AuthenticationExecutionModel.Requirement[] getRequirementChoices();
}

View file

@ -1,11 +1,13 @@
package org.keycloak.authentication;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.UserModel;
import javax.ws.rs.core.Response;
import java.util.Iterator;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -15,11 +17,16 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
Response alternativeChallenge = null;
AuthenticationExecutionModel challengedAlternativeExecution = null;
boolean alternativeSuccessful = false;
Iterator<AuthenticationExecutionModel> executions;
List<AuthenticationExecutionModel> executions;
Iterator<AuthenticationExecutionModel> executionIterator;
AuthenticationProcessor processor;
AuthenticationFlowModel flow;
public DefaultAuthenticationFlow(AuthenticationProcessor processor) {
public DefaultAuthenticationFlow(AuthenticationProcessor processor, AuthenticationFlowModel flow) {
this.processor = processor;
this.flow = flow;
this.executions = processor.getRealm().getAuthenticationExecutions(flow.getId());
this.executionIterator = executions.iterator();
}
protected boolean isProcessed(AuthenticationExecutionModel model) {
@ -34,25 +41,21 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
@Override
public Response processAction(String actionExecution) {
while (executions.hasNext()) {
AuthenticationExecutionModel model = executions.next();
while (executionIterator.hasNext()) {
AuthenticationExecutionModel model = executionIterator.next();
if (isProcessed(model)) {
AuthenticationProcessor.logger.debug("execution is processed");
if (!alternativeSuccessful && model.isAlternative() && processor.isSuccessful(model))
alternativeSuccessful = true;
continue;
}
if (!model.getId().equals(actionExecution)) {
if (model.isAutheticatorFlow()) {
AuthenticationFlow authenticationFlow = processor.createFlowExecution(model.getFlowId(), model);
return authenticationFlow.processAction(actionExecution);
} else {
throw new AuthenticationProcessor.AuthException("action is not current execution", AuthenticationProcessor.Error.INTERNAL_ERROR);
}
} else { // we found the action
} else if (model.getId().equals(actionExecution)) {
AuthenticatorFactory factory = (AuthenticatorFactory) processor.getSession().getKeycloakSessionFactory().getProviderFactory(Authenticator.class, model.getAuthenticator());
Authenticator authenticator = factory.create();
AuthenticatorContext result = processor.createAuthenticatorContext(model, authenticator);
AuthenticatorContext result = processor.createAuthenticatorContext(model, authenticator, executions);
authenticator.action(result);
Response response = processResult(result);
if (response == null) return processFlow();
@ -64,8 +67,8 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
@Override
public Response processFlow() {
while (executions.hasNext()) {
AuthenticationExecutionModel model = executions.next();
while (executionIterator.hasNext()) {
AuthenticationExecutionModel model = executionIterator.next();
if (isProcessed(model)) {
AuthenticationProcessor.logger.debug("execution is processed");
if (!alternativeSuccessful && model.isAlternative() && processor.isSuccessful(model))
@ -132,7 +135,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
}
}
}
AuthenticatorContext context = processor.createAuthenticatorContext(model, authenticator);
AuthenticatorContext context = processor.createAuthenticatorContext(model, authenticator, executions);
authenticator.authenticate(context);
Response response = processResult(context);
if (response != null) return response;

View file

@ -10,7 +10,7 @@ import org.keycloak.provider.Provider;
* @version $Revision: 1 $
*/
public interface FormAction extends Provider {
void authenticate(FormContext context);
void authenticate(FormActionContext context);
boolean requiresUser();
boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user);

View file

@ -0,0 +1,12 @@
package org.keycloak.authentication;
import org.keycloak.models.AuthenticationExecutionModel;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface FormActionContext extends AuthenticatorContext {
FormAuthenticator getFormAuthenticator();
AuthenticationExecutionModel getFormExecution();
}

View file

@ -6,5 +6,5 @@ import org.keycloak.provider.ProviderFactory;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface FormActionFactory extends ProviderFactory<FormAction> {
public interface FormActionFactory extends ProviderFactory<FormAction>, ConfigurableAuthenticatorFactory {
}

View file

@ -14,8 +14,10 @@ import org.keycloak.services.managers.BruteForceProtector;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.util.Iterator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -23,26 +25,35 @@ import java.util.List;
*/
public class FormAuthenticationFlow implements AuthenticationFlow {
AuthenticationProcessor processor;
AuthenticationExecutionModel execution;
AuthenticationExecutionModel formExecution;
private final List<AuthenticationExecutionModel> formActionExecutions;
private final FormAuthenticator formAuthenticator;
public FormAuthenticationFlow(AuthenticationProcessor processor, AuthenticationExecutionModel execution) {
this.processor = processor;
this.execution = execution;
this.formExecution = execution;
formActionExecutions = processor.getRealm().getAuthenticationExecutions(execution.getFlowId());
formAuthenticator = processor.getSession().getProvider(FormAuthenticator.class, execution.getAuthenticator());
}
private static class FormActionResult implements FormContext {
AuthenticatorContext delegate;
FormAuthenticator authenticator;
private class FormContext implements FormActionContext {
protected AuthenticatorContext delegate;
FormActionResult(AuthenticatorContext delegate, FormAuthenticator authenticator) {
private FormContext(AuthenticatorContext delegate) {
this.delegate = delegate;
this.authenticator = authenticator;
}
@Override
public FormAuthenticator getFormAuthenticator() {
return authenticator;
return formAuthenticator;
}
@Override
public AuthenticationExecutionModel getFormExecution() {
return formExecution;
}
@Override
@ -135,6 +146,18 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
return delegate.getProtector();
}
@Override
public AuthenticationExecutionModel.Requirement getCategoryRequirementFromCurrentFlow(String authenticatorCategory) {
for (AuthenticationExecutionModel formActionExecution : formActionExecutions) {
FormActionFactory factory = (FormActionFactory) getSession().getKeycloakSessionFactory().getProviderFactory(FormAction.class, formActionExecution.getAuthenticator());
if (factory != null && authenticatorCategory.equals(factory.getReferenceCategory())) {
return formActionExecution.getRequirement();
}
}
return null;
}
@Override
public void success() {
delegate.success();
@ -191,19 +214,19 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
}
}
@Override
public Response processAction(String actionExecution) {
if (!actionExecution.equals(execution.getId())) {
if (!actionExecution.equals(formExecution.getId())) {
throw new AuthenticationProcessor.AuthException("action is not current execution", AuthenticationProcessor.Error.INTERNAL_ERROR);
}
FormAuthenticator authenticator = processor.getSession().getProvider(FormAuthenticator.class, execution.getAuthenticator());
for (AuthenticationExecutionModel formActionExecution : processor.getRealm().getAuthenticationExecutions(execution.getFlowId())) {
FormAction action = processor.getSession().getProvider(FormAction.class, execution.getAuthenticator());
Map<String, ClientSessionModel.ExecutionStatus> executionStatus = new HashMap<>();
List<FormAction> requiredActions = new LinkedList<>();
for (AuthenticationExecutionModel formActionExecution : formActionExecutions) {
FormAction action = processor.getSession().getProvider(FormAction.class, formActionExecution.getAuthenticator());
UserModel authUser = processor.getClientSession().getAuthenticatedUser();
if (action.requiresUser() && authUser == null) {
throw new AuthenticationProcessor.AuthException("form action: " + execution.getAuthenticator() + " requires user", AuthenticationProcessor.Error.UNKNOWN_USER);
throw new AuthenticationProcessor.AuthException("form action: " + formExecution.getAuthenticator() + " requires user", AuthenticationProcessor.Error.UNKNOWN_USER);
}
boolean configuredFor = false;
if (action.requiresUser() && authUser != null) {
@ -211,23 +234,33 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
if (!configuredFor) {
if (formActionExecution.isRequired()) {
if (formActionExecution.isUserSetupAllowed()) {
AuthenticationProcessor.logger.debugv("authenticator SETUP_REQUIRED: {0}", execution.getAuthenticator());
processor.getClientSession().setExecutionStatus(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.SETUP_REQUIRED);
action.setRequiredActions(processor.getSession(), processor.getRealm(), authUser);
AuthenticationProcessor.logger.debugv("authenticator SETUP_REQUIRED: {0}", formExecution.getAuthenticator());
executionStatus.put(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.SETUP_REQUIRED);
requiredActions.add(action);
continue;
} else {
throw new AuthenticationProcessor.AuthException(AuthenticationProcessor.Error.CREDENTIAL_SETUP_REQUIRED);
}
} else if (formActionExecution.isOptional()) {
processor.getClientSession().setExecutionStatus(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
executionStatus.put(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
continue;
}
}
}
FormActionResult result = new FormActionResult(processor.createAuthenticatorContext(formActionExecution, null), authenticator);
AuthenticatorContext delegate = processor.createAuthenticatorContext(formActionExecution, null, formActionExecutions);
FormActionContext result = new FormContext(delegate);
action.authenticate(result);
return processResult(result, formActionExecution);
Response challenge = processResult(executionStatus, result, formActionExecution);
if (challenge != null) return challenge;
executionStatus.put(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.SUCCESS);
}
// set status and required actions only if form is fully successful
for (Map.Entry<String, ClientSessionModel.ExecutionStatus> entry : executionStatus.entrySet()) {
processor.getClientSession().setExecutionStatus(entry.getKey(), entry.getValue());
}
for (FormAction action : requiredActions) {
action.setRequiredActions(processor.getSession(), processor.getRealm(), processor.getClientSession().getAuthenticatedUser());
}
return null;
@ -236,42 +269,48 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
@Override
public Response processFlow() {
FormAuthenticator authenticator = processor.getSession().getProvider(FormAuthenticator.class, execution.getAuthenticator());
AuthenticatorContext context = processor.createAuthenticatorContext(execution, null);
authenticator.authenticate(context);
return processResult(context, execution);
AuthenticatorContext delegate = processor.createAuthenticatorContext(formExecution, null, formActionExecutions);
FormActionContext result = new FormContext(delegate);
formAuthenticator.authenticate(result);
Map<String, ClientSessionModel.ExecutionStatus> executionStatus = new HashMap<>();
Response response = processResult(executionStatus, result, formExecution);
for (Map.Entry<String, ClientSessionModel.ExecutionStatus> entry : executionStatus.entrySet()) {
processor.getClientSession().setExecutionStatus(entry.getKey(), entry.getValue());
}
return response;
}
public Response processResult(AuthenticatorContext result, AuthenticationExecutionModel execution) {
public Response processResult(Map<String, ClientSessionModel.ExecutionStatus> executionStatus, AuthenticatorContext result, AuthenticationExecutionModel execution) {
AuthenticationProcessor.Status status = result.getStatus();
if (status == AuthenticationProcessor.Status.SUCCESS) {
executionStatus.put(execution.getId(), ClientSessionModel.ExecutionStatus.SUCCESS);
return null;
} else if (status == AuthenticationProcessor.Status.FAILED) {
AuthenticationProcessor.logger.debugv("authenticator FAILED: {0}", execution.getAuthenticator());
processor.logFailure();
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.FAILED);
executionStatus.put(execution.getId(), ClientSessionModel.ExecutionStatus.FAILED);
if (result.getChallenge() != null) {
return sendChallenge(result, execution);
return sendChallenge(result);
}
throw new AuthenticationProcessor.AuthException(result.getError());
} else if (status == AuthenticationProcessor.Status.FORCE_CHALLENGE) {
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
return sendChallenge(result, execution);
executionStatus.put(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
return sendChallenge(result);
} else if (status == AuthenticationProcessor.Status.CHALLENGE) {
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
return sendChallenge(result, execution);
return sendChallenge(result);
} else if (status == AuthenticationProcessor.Status.FAILURE_CHALLENGE) {
AuthenticationProcessor.logger.debugv("authenticator FAILURE_CHALLENGE: {0}", execution.getAuthenticator());
processor.logFailure();
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
return sendChallenge(result, execution);
executionStatus.put(execution.getId(), ClientSessionModel.ExecutionStatus.CHALLENGED);
return sendChallenge(result);
} else if (status == AuthenticationProcessor.Status.ATTEMPTED) {
AuthenticationProcessor.logger.debugv("authenticator ATTEMPTED: {0}", execution.getAuthenticator());
if (execution.getRequirement() == AuthenticationExecutionModel.Requirement.REQUIRED) {
throw new AuthenticationProcessor.AuthException(AuthenticationProcessor.Error.INVALID_CREDENTIALS);
}
processor.getClientSession().setExecutionStatus(execution.getId(), ClientSessionModel.ExecutionStatus.ATTEMPTED);
executionStatus.put(execution.getId(), ClientSessionModel.ExecutionStatus.ATTEMPTED);
return null;
} else {
AuthenticationProcessor.logger.debugv("authenticator INTERNAL_ERROR: {0}", execution.getAuthenticator());
@ -281,8 +320,8 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
}
public Response sendChallenge(AuthenticatorContext result, AuthenticationExecutionModel execution) {
processor.getClientSession().setNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, execution.getId());
public Response sendChallenge(AuthenticatorContext result) {
processor.getClientSession().setNote(AuthenticationProcessor.CURRENT_AUTHENTICATION_EXECUTION, formExecution.getId());
return result.getChallenge();
}

View file

@ -1,8 +1,11 @@
package org.keycloak.authentication;
import org.keycloak.models.utils.FormMessage;
import org.keycloak.provider.Provider;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -10,5 +13,5 @@ import javax.ws.rs.core.Response;
*/
public interface FormAuthenticator extends Provider {
void authenticate(AuthenticatorContext context);
Response createChallenge(FormContext context, String... errorMessages);
Response createChallenge(FormActionContext context, MultivaluedMap<String, String> formData, List<FormMessage> errorMessages);
}

View file

@ -6,5 +6,5 @@ import org.keycloak.provider.ProviderFactory;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface FormAuthenticatorFactory extends ProviderFactory<FormAuthenticator> {
public interface FormAuthenticatorFactory extends ProviderFactory<FormAuthenticator>, ConfigurableAuthenticatorFactory {
}

View file

@ -1,9 +0,0 @@
package org.keycloak.authentication;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface FormContext extends AuthenticatorContext {
FormAuthenticator getFormAuthenticator();
}

View file

@ -49,7 +49,7 @@ public class CookieAuthenticatorFactory implements AuthenticatorFactory {
}
@Override
public String getReferenceType() {
public String getReferenceCategory() {
return "cookie";
}

View file

@ -51,7 +51,7 @@ public class OTPFormAuthenticatorFactory implements AuthenticatorFactory {
}
@Override
public String getReferenceType() {
public String getReferenceCategory() {
return UserCredentialModel.TOTP;
}

View file

@ -46,7 +46,7 @@ public class SpnegoAuthenticatorFactory implements AuthenticatorFactory {
}
@Override
public String getReferenceType() {
public String getReferenceCategory() {
return UserCredentialModel.KERBEROS;
}

View file

@ -51,7 +51,7 @@ public class UsernamePasswordFormFactory implements AuthenticatorFactory {
}
@Override
public String getReferenceType() {
public String getReferenceCategory() {
return UserCredentialModel.PASSWORD;
}

View file

@ -0,0 +1,116 @@
package org.keycloak.authentication.forms;
import org.keycloak.Config;
import org.keycloak.OAuth2Constants;
import org.keycloak.authentication.AuthenticatorContext;
import org.keycloak.authentication.FormActionContext;
import org.keycloak.authentication.FormAuthenticator;
import org.keycloak.authentication.FormAuthenticatorFactory;
import org.keycloak.login.LoginFormsProvider;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.utils.FormMessage;
import org.keycloak.services.resources.LoginActionsService;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import java.net.URI;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RegistrationPage implements FormAuthenticator, FormAuthenticatorFactory {
public static final String EXECUTION = "execution";
public static final String FIELD_PASSWORD_CONFIRM = "password-confirm";
public static final String FIELD_PASSWORD = "password";
public static final String FIELD_EMAIL = "email";
public static final String FIELD_USERNAME = "username";
public static final String FIELD_LAST_NAME = "lastName";
public static final String FIELD_FIRST_NAME = "firstName";
public static final String PROVIDER_ID = "registration-page-form";
@Override
public void authenticate(AuthenticatorContext context) {
LoginFormsProvider registrationPage = createForm(context, context.getExecution().getId());
context.challenge(registrationPage.createRegistration());
}
public URI getActionUrl(AuthenticatorContext context, String executionId, String code) {
return LoginActionsService.registrationFormProcessor(context.getUriInfo())
.queryParam(OAuth2Constants.CODE, code)
.queryParam(EXECUTION, executionId)
.build(context.getRealm().getName());
}
@Override
public Response createChallenge(FormActionContext context, MultivaluedMap<String, String> formData, List<FormMessage> errorMessages) {
LoginFormsProvider registrationPage = createForm(context, context.getFormExecution().getId());
if (formData != null) registrationPage.setFormData(formData);
if (errorMessages != null) {
registrationPage.setErrors(errorMessages);
}
return registrationPage.createRegistration();
}
public LoginFormsProvider createForm(AuthenticatorContext context, String executionId) {
AuthenticationExecutionModel.Requirement categoryRequirement = context.getCategoryRequirementFromCurrentFlow(UserCredentialModel.PASSWORD);
boolean passwordRequired = categoryRequirement != null && categoryRequirement != AuthenticationExecutionModel.Requirement.DISABLED;
String code = context.generateAccessCode();
URI actionUrl = getActionUrl(context, executionId, code);
return context.getSession().getProvider(LoginFormsProvider.class)
.setAttribute("passwordRequired", passwordRequired)
.setActionUri(actionUrl)
.setClientSessionCode(code);
}
@Override
public void close() {
}
@Override
public String getDisplayType() {
return "Registration Page";
}
@Override
public String getReferenceCategory() {
return null;
}
@Override
public boolean isConfigurable() {
return false;
}
@Override
public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
return new AuthenticationExecutionModel.Requirement[0];
}
@Override
public FormAuthenticator create(KeycloakSession session) {
return this;
}
@Override
public void init(Config.Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public String getId() {
return PROVIDER_ID;
}
}

View file

@ -0,0 +1,117 @@
package org.keycloak.authentication.forms;
import org.keycloak.Config;
import org.keycloak.authentication.AuthenticatorContext;
import org.keycloak.authentication.FormAction;
import org.keycloak.authentication.FormActionContext;
import org.keycloak.authentication.FormActionFactory;
import org.keycloak.authentication.FormAuthenticator;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.FormMessage;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.validation.Validation;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import java.util.ArrayList;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RegistrationPasswordValidation implements FormAction, FormActionFactory {
public static final String PROVIDER_ID = "password-validation-action";
@Override
public void authenticate(FormActionContext context) {
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
List<FormMessage> errors = new ArrayList<>();
if (Validation.isBlank(formData.getFirst(RegistrationPage.FIELD_PASSWORD))) {
errors.add(new FormMessage(RegistrationPage.FIELD_PASSWORD, Messages.MISSING_PASSWORD));
} else if (!formData.getFirst(RegistrationPage.FIELD_PASSWORD).equals(formData.getFirst(RegistrationPage.FIELD_PASSWORD_CONFIRM))) {
errors.add(new FormMessage(RegistrationPage.FIELD_PASSWORD_CONFIRM, Messages.INVALID_PASSWORD_CONFIRM));
}
if (formData.getFirst(RegistrationPage.FIELD_PASSWORD) != null) {
PasswordPolicy.Error err = context.getRealm().getPasswordPolicy().validate(context.getRealm().isRegistrationEmailAsUsername() ? formData.getFirst(RegistrationPage.FIELD_EMAIL) : formData.getFirst(RegistrationPage.FIELD_USERNAME), formData.getFirst(RegistrationPage.FIELD_PASSWORD));
if (err != null)
errors.add(new FormMessage(RegistrationPage.FIELD_PASSWORD, err.getMessage(), err.getParameters()));
}
if (errors.size() > 0) {
formData.remove(RegistrationPage.FIELD_PASSWORD);
formData.remove(RegistrationPage.FIELD_PASSWORD_CONFIRM);
Response challenge = context.getFormAuthenticator().createChallenge(context, formData, errors);
context.challenge(challenge);
return;
} else {
context.success();
}
}
@Override
public boolean requiresUser() {
return false;
}
@Override
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
return true;
}
@Override
public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
}
@Override
public void close() {
}
@Override
public String getDisplayType() {
return "Password Validation";
}
@Override
public String getReferenceCategory() {
return UserCredentialModel.PASSWORD;
}
@Override
public boolean isConfigurable() {
return false;
}
@Override
public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
return new AuthenticationExecutionModel.Requirement[0];
}
@Override
public FormAction create(KeycloakSession session) {
return this;
}
@Override
public void init(Config.Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public String getId() {
return PROVIDER_ID;
}
}

View file

@ -0,0 +1,132 @@
package org.keycloak.authentication.forms;
import org.keycloak.Config;
import org.keycloak.authentication.AuthenticatorContext;
import org.keycloak.authentication.FormAction;
import org.keycloak.authentication.FormActionContext;
import org.keycloak.authentication.FormActionFactory;
import org.keycloak.authentication.FormAuthenticator;
import org.keycloak.events.Errors;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.FormMessage;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.validation.Validation;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import java.util.ArrayList;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RegistrationProfileValidation implements FormAction, FormActionFactory {
public static final String PROVIDER_ID = "profile-validation-action";
@Override
public void authenticate(FormActionContext context) {
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
List<FormMessage> errors = new ArrayList<>();
String eventError = Errors.INVALID_REGISTRATION;
if (Validation.isBlank(formData.getFirst((RegistrationPage.FIELD_FIRST_NAME)))) {
errors.add(new FormMessage(RegistrationPage.FIELD_FIRST_NAME, Messages.MISSING_FIRST_NAME));
}
if (Validation.isBlank(formData.getFirst((RegistrationPage.FIELD_LAST_NAME)))) {
errors.add(new FormMessage(RegistrationPage.FIELD_LAST_NAME, Messages.MISSING_LAST_NAME));
}
String email = formData.getFirst(Validation.FIELD_EMAIL);
if (Validation.isBlank(email)) {
errors.add(new FormMessage(RegistrationPage.FIELD_EMAIL, Messages.MISSING_EMAIL));
} else if (!Validation.isEmailValid(email)) {
formData.remove(Validation.FIELD_EMAIL);
errors.add(new FormMessage(RegistrationPage.FIELD_EMAIL, Messages.INVALID_EMAIL));
}
if (context.getSession().users().getUserByEmail(email, context.getRealm()) != null) {
eventError = Errors.EMAIL_IN_USE;
formData.remove(Validation.FIELD_EMAIL);
errors.add(new FormMessage(RegistrationPage.FIELD_EMAIL, Messages.EMAIL_EXISTS));
}
if (errors.size() > 0) {
context.getEvent().error(eventError);
Response challenge = context.getFormAuthenticator().createChallenge(context, formData, errors);
context.challenge(challenge);
return;
} else {
context.success();
}
}
@Override
public boolean requiresUser() {
return false;
}
@Override
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
return true;
}
@Override
public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
}
@Override
public void close() {
}
@Override
public String getDisplayType() {
return "Profile Validation";
}
@Override
public String getReferenceCategory() {
return null;
}
@Override
public boolean isConfigurable() {
return false;
}
@Override
public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
return new AuthenticationExecutionModel.Requirement[0];
}
@Override
public FormAction create(KeycloakSession session) {
return this;
}
@Override
public void init(Config.Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public String getId() {
return PROVIDER_ID;
}
}

View file

@ -0,0 +1,126 @@
package org.keycloak.authentication.forms;
import org.keycloak.Config;
import org.keycloak.authentication.AuthenticatorContext;
import org.keycloak.authentication.FormAction;
import org.keycloak.authentication.FormActionContext;
import org.keycloak.authentication.FormActionFactory;
import org.keycloak.authentication.FormAuthenticator;
import org.keycloak.login.LoginFormsProvider;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.ModelException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.resources.AttributeFormDataProcessor;
import org.keycloak.services.validation.Validation;
import javax.ws.rs.core.MultivaluedMap;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RegistrationUserCreation implements FormAction, FormActionFactory {
public static final String PROVIDER_ID = "registration-user-creation";
@Override
public void authenticate(FormActionContext context) {
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
String email = formData.getFirst(Validation.FIELD_EMAIL);
String username = formData.getFirst(RegistrationPage.FIELD_USERNAME);
if (context.getRealm().isRegistrationEmailAsUsername()) {
username = formData.getFirst(RegistrationPage.FIELD_EMAIL);
}
UserModel user = context.getSession().users().addUser(context.getRealm(), username);
user.setEnabled(true);
user.setFirstName(formData.getFirst("firstName"));
user.setLastName(formData.getFirst("lastName"));
user.setEmail(email);
context.getClientSession().setNote(OIDCLoginProtocol.LOGIN_HINT_PARAM, username);
AttributeFormDataProcessor.process(formData, context.getRealm(), user);
context.setUser(user);
AuthenticationExecutionModel.Requirement categoryRequirement = context.getCategoryRequirementFromCurrentFlow(UserCredentialModel.PASSWORD);
boolean passwordRequired = categoryRequirement != null && categoryRequirement != AuthenticationExecutionModel.Requirement.DISABLED;
if (passwordRequired) {
String password = formData.getFirst(RegistrationPage.FIELD_PASSWORD);
UserCredentialModel credentials = new UserCredentialModel();
credentials.setType(CredentialRepresentation.PASSWORD);
credentials.setValue(password);
try {
context.getSession().users().updateCredential(context.getRealm(), user, UserCredentialModel.password(formData.getFirst("password")));
} catch (Exception me) {
user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
}
}
context.getEvent().user(user);
context.success();
}
@Override
public boolean requiresUser() {
return false;
}
@Override
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
return true;
}
@Override
public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
}
@Override
public void close() {
}
@Override
public String getDisplayType() {
return "Registration User Creation";
}
@Override
public String getReferenceCategory() {
return null;
}
@Override
public boolean isConfigurable() {
return false;
}
@Override
public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
return new AuthenticationExecutionModel.Requirement[0];
}
@Override
public FormAction create(KeycloakSession session) {
return this;
}
@Override
public void init(Config.Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public String getId() {
return PROVIDER_ID;
}
}

View file

@ -0,0 +1,147 @@
package org.keycloak.authentication.forms;
import org.keycloak.Config;
import org.keycloak.authentication.AuthenticatorContext;
import org.keycloak.authentication.FormAction;
import org.keycloak.authentication.FormActionContext;
import org.keycloak.authentication.FormActionFactory;
import org.keycloak.authentication.FormAuthenticator;
import org.keycloak.events.Errors;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.FormMessage;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.validation.Validation;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import java.util.ArrayList;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RegistrationUsernameValidation implements FormAction, FormActionFactory {
public static final String PROVIDER_ID = "username-validation-action";
@Override
public void authenticate(FormActionContext context) {
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
List<FormMessage> errors = new ArrayList<>();
String email = formData.getFirst(Validation.FIELD_EMAIL);
String username = formData.getFirst(RegistrationPage.FIELD_USERNAME);
String usernameField = RegistrationPage.FIELD_USERNAME;
if (context.getRealm().isRegistrationEmailAsUsername()) {
username = email;
usernameField = RegistrationPage.FIELD_EMAIL;
if (Validation.isBlank(email)) {
errors.add(new FormMessage(RegistrationPage.FIELD_EMAIL, Messages.MISSING_EMAIL));
} else if (!Validation.isEmailValid(email)) {
errors.add(new FormMessage(RegistrationPage.FIELD_EMAIL, Messages.INVALID_EMAIL));
formData.remove(Validation.FIELD_EMAIL);
}
if (errors.size() > 0) {
context.getEvent().error(Errors.INVALID_REGISTRATION);
Response challenge = context.getFormAuthenticator().createChallenge(context, formData, errors);
context.challenge(challenge);
return;
}
if (email != null && context.getSession().users().getUserByEmail(email, context.getRealm()) != null) {
context.getEvent().error(Errors.EMAIL_IN_USE);
formData.remove(Validation.FIELD_EMAIL);
errors.add(new FormMessage(RegistrationPage.FIELD_EMAIL, Messages.EMAIL_EXISTS));
Response challenge = context.getFormAuthenticator().createChallenge(context, formData, errors);
context.challenge(challenge);
return;
}
} else {
if (Validation.isBlank(username)) {
context.getEvent().error(Errors.INVALID_REGISTRATION);
errors.add(new FormMessage(RegistrationPage.FIELD_USERNAME, Messages.MISSING_USERNAME));
Response challenge = context.getFormAuthenticator().createChallenge(context, formData, errors);
context.challenge(challenge);
return;
}
}
if (context.getSession().users().getUserByUsername(username, context.getRealm()) != null) {
context.getEvent().error(Errors.USERNAME_IN_USE);
errors.add(new FormMessage(usernameField, Messages.USERNAME_EXISTS));
formData.remove(Validation.FIELD_USERNAME);
formData.remove(Validation.FIELD_EMAIL);
Response challenge = context.getFormAuthenticator().createChallenge(context, formData, errors);
context.challenge(challenge);
return;
}
context.success();
}
@Override
public boolean requiresUser() {
return false;
}
@Override
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
return true;
}
@Override
public void setRequiredActions(KeycloakSession session, RealmModel realm, UserModel user) {
}
@Override
public void close() {
}
@Override
public String getDisplayType() {
return "Username Validation";
}
@Override
public String getReferenceCategory() {
return null;
}
@Override
public boolean isConfigurable() {
return false;
}
@Override
public AuthenticationExecutionModel.Requirement[] getRequirementChoices() {
return new AuthenticationExecutionModel.Requirement[0];
}
@Override
public FormAction create(KeycloakSession session) {
return this;
}
@Override
public void init(Config.Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public String getId() {
return PROVIDER_ID;
}
}

View file

@ -29,7 +29,7 @@ import java.util.Set;
public class DefaultKeycloakSession implements KeycloakSession {
private final DefaultKeycloakSessionFactory factory;
private final Map<Integer, Provider> providers = new HashMap<Integer, Provider>();
private final Map<Integer, Provider> providers = new HashMap<>();
private final List<Provider> closable = new LinkedList<Provider>();
private final DefaultKeycloakTransactionManager transactionManager;
private RealmProvider model;

View file

@ -130,6 +130,10 @@ public class LoginActionsService {
return loginActionsBaseUrl(uriInfo).path(LoginActionsService.class, "authenticateForm");
}
public static UriBuilder registrationFormProcessor(UriInfo uriInfo) {
return loginActionsBaseUrl(uriInfo).path(LoginActionsService.class, "processRegister");
}
public static UriBuilder loginActionsBaseUrl(UriBuilder baseUriBuilder) {
return baseUriBuilder.path(RealmsResource.class).path(RealmsResource.class, "getLoginActionsService");
}
@ -269,6 +273,10 @@ public class LoginActionsService {
protected Response processAuthentication(String execution, ClientSessionModel clientSession) {
String flowAlias = DefaultAuthenticationFlows.BROWSER_FLOW;
return processFlow(execution, clientSession, flowAlias);
}
protected Response processFlow(String execution, ClientSessionModel clientSession, String flowAlias) {
AuthenticationFlowModel flow = realm.getFlowByAlias(flowAlias);
AuthenticationProcessor processor = new AuthenticationProcessor();
processor.setClientSession(clientSession)
@ -313,6 +321,12 @@ public class LoginActionsService {
return processAuthentication(execution, clientSession);
}
protected Response processRegistration(String execution, ClientSessionModel clientSession) {
String flowAlias = DefaultAuthenticationFlows.REGISTRATION_FLOW;
return processFlow(execution, clientSession, flowAlias);
}
/**
* protocol independent registration page entry point
@ -322,7 +336,8 @@ public class LoginActionsService {
*/
@Path("registration")
@GET
public Response registerPage(@QueryParam("code") String code) {
public Response registerPage(@QueryParam("code") String code,
@QueryParam("execution") String execution) {
event.event(EventType.REGISTER);
if (!realm.isRegistrationAllowed()) {
event.error(Errors.REGISTRATION_DISABLED);
@ -330,7 +345,7 @@ public class LoginActionsService {
}
Checks checks = new Checks();
if (!checks.check(code)) {
if (!checks.check(code, ClientSessionModel.Action.AUTHENTICATE.name())) {
return checks.response;
}
event.detail(Details.CODE_ID, code);
@ -340,10 +355,7 @@ public class LoginActionsService {
authManager.expireIdentityCookie(realm, uriInfo, clientConnection);
return session.getProvider(LoginFormsProvider.class)
.setClientSessionCode(clientSessionCode.getCode())
.setAttribute("passwordRequired", isPasswordRequired())
.createRegistration();
return processRegistration(execution, clientSession);
}
@ -353,10 +365,10 @@ public class LoginActionsService {
* @param code
* @return
*/
@Path("request/registration")
@Path("registration")
@POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response processRegister(@QueryParam("code") String code) {
public Response processRegister(@QueryParam("code") String code,
@QueryParam("execution") String execution) {
event.event(EventType.REGISTER);
Checks checks = new Checks();
if (!checks.check(code, ClientSessionModel.Action.AUTHENTICATE.name())) {
@ -367,6 +379,13 @@ public class LoginActionsService {
return ErrorPage.error(session, Messages.REGISTRATION_NOT_ALLOWED);
}
ClientSessionCode clientCode = checks.clientCode;
ClientSessionModel clientSession = clientCode.getClientSession();
return processRegistration(execution, clientSession);
}
public Response oldRegistration(ClientSessionCode clientCode, ClientSessionModel clientSession) {
MultivaluedMap<String, String> formData = request.getDecodedFormParameters();
String username = formData.getFirst(Validation.FIELD_USERNAME);
String email = formData.getFirst(Validation.FIELD_EMAIL);
@ -374,8 +393,6 @@ public class LoginActionsService {
username = email;
formData.putSingle(AuthenticationManager.FORM_USERNAME, username);
}
ClientSessionCode clientCode = checks.clientCode;
ClientSessionModel clientSession = clientCode.getClientSession();
event.client(clientSession.getClient())
.detail(Details.REDIRECT_URI, clientSession.getRedirectUri())
.detail(Details.RESPONSE_TYPE, "code")
@ -454,11 +471,11 @@ public class LoginActionsService {
// User already registered, but force him to update password
if (!passwordUpdateSuccessful) {
user.addRequiredAction(UserModel.RequiredAction.UPDATE_PASSWORD);
user.addRequiredAction(RequiredAction.UPDATE_PASSWORD);
return session.getProvider(LoginFormsProvider.class)
.setError(passwordUpdateError, passwordUpdateErrorParameters)
.setClientSessionCode(clientCode.getCode())
.createResponse(UserModel.RequiredAction.UPDATE_PASSWORD);
.createResponse(RequiredAction.UPDATE_PASSWORD);
}
}
clientSession.setNote(OIDCLoginProtocol.LOGIN_HINT_PARAM, username);

View file

@ -8,7 +8,6 @@ import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.authentication.AuthenticatorUtil;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.AuthenticatorConfigModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredActionProviderModel;
@ -139,8 +138,8 @@ public class AuthenticationManagementResource {
rep.setSubFlow(true);
}
AuthenticatorFactory factory = (AuthenticatorFactory)session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, execution.getAuthenticator());
if (factory.getReferenceType() == null) continue;
rep.setReferenceType(factory.getReferenceType());
if (factory.getReferenceCategory() == null) continue;
rep.setReferenceType(factory.getReferenceCategory());
rep.setConfigurable(factory.isConfigurable());
for (AuthenticationExecutionModel.Requirement choice : factory.getRequirementChoices()) {
rep.getRequirementChoices().add(choice.name());

View file

@ -0,0 +1,4 @@
org.keycloak.authentication.forms.RegistrationPasswordValidation
org.keycloak.authentication.forms.RegistrationProfileValidation
org.keycloak.authentication.forms.RegistrationUserCreation
org.keycloak.authentication.forms.RegistrationUsernameValidation

View file

@ -0,0 +1 @@
org.keycloak.authentication.forms.RegistrationPage

View file

@ -166,7 +166,7 @@ public class AccountTest {
});
}
//@Test
@Test
public void ideTesting() throws Exception {
Thread.sleep(100000000);
}

View file

@ -5,7 +5,6 @@ import org.keycloak.authentication.authenticators.SpnegoAuthenticatorFactory;
import org.keycloak.authentication.authenticators.UsernamePasswordFormFactory;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.AuthenticatorConfigModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.DefaultAuthenticationFlows;
import org.keycloak.representations.idm.CredentialRepresentation;
@ -29,7 +28,7 @@ public class CredentialHelper {
public static void setCredentialRequirement(String type, RealmModel realm, AuthenticationExecutionModel.Requirement requirement) {
if (type.equals(CredentialRepresentation.TOTP)) {
String providerId = OTPFormAuthenticatorFactory.PROVIDER_ID;
String flowAlias = DefaultAuthenticationFlows.FORMS_FLOW;
String flowAlias = DefaultAuthenticationFlows.LOGIN_FORMS_FLOW;
authenticationRequirement(realm, providerId, flowAlias, requirement);
} else if (type.equals(CredentialRepresentation.KERBEROS)) {
String providerId = SpnegoAuthenticatorFactory.PROVIDER_ID;
@ -37,7 +36,7 @@ public class CredentialHelper {
authenticationRequirement(realm, providerId, flowAlias, requirement);
} else if (type.equals(CredentialRepresentation.PASSWORD)) {
String providerId = UsernamePasswordFormFactory.PROVIDER_ID;
String flowAlias = DefaultAuthenticationFlows.FORMS_FLOW;
String flowAlias = DefaultAuthenticationFlows.LOGIN_FORMS_FLOW;
authenticationRequirement(realm, providerId, flowAlias, requirement);
}
}