Keycloak is without a doubt most feature rich product when it comes to Enterprise security implementation. Recently I had to add custom fields in the Keycloak signup form with a set of rules to validate the data. To my surprise, the implementation was pretty easy. Keycloak out of the box supports customization and validation of forms with the help of FormAction and FormActionFactory.

This article extends the feature of adding custom attributes to signup form. We will learn how to implement validation for the custom fields.
Java Project
Create a Java project with Maven a build tool. The quickest way is to use maven CLI to generate one. Refer following command
mvn archetype:generate -DarchetypeGroupId=org.apache.maven.archetypes -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4
If you have maven on PATH environment variable above command should work and it will prompt you for project details. Enter group id (com.carbonrider.keycloak), artifact id(form-extension), package name (com.carbonrider.keycloak), version(1.0-SNAPSHOT), etc and a skeleton will be generated. The generated folder structure should look like following

Maven Dependency
To use Keycloak interfaces or APIs we must add the appropriate dependencies to pom.xml. First, add a property to properties tag and also change maven.compiler.source and maven.compiler.target to 1.8. Refer to the following snippet
UTF-8
1.8
1.8
4.8.3.Final
Next, you should add keycloak dependency to dependencies section.
junit
junit
4.11
test
org.keycloak
keycloak-core
${keycloak.version}
provided
org.keycloak
keycloak-server-spi
${keycloak.version}
provided
org.keycloak
keycloak-services
${keycloak.version}
provided
org.keycloak
keycloak-server-spi-private
${keycloak.version}
provided
org.jboss.spec.javax.transaction
jboss-transaction-api_1.2_spec
provided
${jboss-transaction-api_1.2_spec}
org.jboss.resteasy
resteasy-jaxrs
provided
${resteasy.version}
org.bouncycastle
bcprov-jdk15on
provided
${bouncycastle.version}
org.bouncycastle
bcpkix-jdk15on
provided
${bouncycastle.version}
org.jboss.logging
jboss-logging
provided
${jboss.logging.version}
org.apache.httpcomponents
httpclient
provided
${apache.httpcomponents.version}
Note that the keycloak.version is referenced from the property that we have defined earlier. Import the project into Eclipse (or IDE of your choice) and then make sure the IDE has imported all the required dependencies.
FormAction
Assuming that the Eclipse project is in stable state, we should proceed with creation of FormAction class. Create a new class inside a package ‘com.carbonrider.keycloak.form’ as ‘SignupFormAction’.
Also import FormAction and FormActionFactory interfaces. Refer below screenshot for details.

FormAction interface is quite interesting. It gives you the ability to customize the form when it is being generated. The callback methods are invoked, which can be used to set attributes to customize form. Recaptcha is an example of FormAction implementation.
Default Implementation
Well, in our case we will be using FormAction to add the validation to the custom form.
To begin with add two constants to SignupFormAction
private static final String PROVIDER_ID = "signup-field-validation-action";
private static Requirement[] REQUIREMENT_CHOICES = { Requirement.REQUIRED, Requirement.DISABLED };
The first constant assigns Unique ID for our Form action, while the second constant indicates that the action can be marked as required or disabled. You can make it optional too. Make sure that you return the id from getId method. Also, the REQUIREMENT_CHOICES should be returned from getRequirementChoices method.
Return a meaningful text from getDisplayType and getHelpText method. The text will be displayed while configuring form action in admin panel.
Validation
Let me skip a few methods and quickly jump to Validation. The validation for the form can be applied using the validate method. Refer below snippet
MultivaluedMap formData = context.getHttpRequest().getDecodedFormParameters();
List errors = new ArrayList<>();
String eventError = Errors.INVALID_REGISTRATION;
You can refer to the http request parameters using the context object. The context object is very useful and provides lot of details about the execution environment (realm, client details etc). You can customize the logic based on your application requirements.
Next we add the logic to check whether the custom fields have valid values.
if (isBlank(formData.getFirst("user.attributes.aliasName"))) {
errors.add(new FormMessage("user.attributes.aliasName", "missingAliasNameMessage"));
}
if (isBlank(formData.getFirst("user.attributes.twitterURL"))) {
errors.add(new FormMessage("user.attributes.twitterURL", "missingTwitterURLMessage"));
}
In the above code snippet, we are just checking if the values are blank. If so, an error message is added to the collection. Note the first argument is the name of the field while the second argument is a key in properties file containing an error message. The property file is present in the current theme that you have selected and should be available at THEME_NAME\login\messages\messages_en.properties. Add the above properties and an appropriate message will be displayed at runtime.
Building JAR and deployment
Since the validation logic is complete, we must deploy it in keycloak to bring it in action. Refer to below steps
- Execute `mvn package` command to build the jar. The JAR must avoid bundling packages that come with Keycloak. You will notice that most of the dependencies in our pom.xml are marked as provided.
- Copy the generated JAR file to KEYCLOAK_INSTALLATION_FOLDER\standalone\deployments
- Make sure that you have copied or updated the properties file in Keycloak Themes. The Theme (customized) folder must be placed in KEYCLOAK_INSTALLATION_FOLDER\themes. If you are using pre-configured themes, make sure that the appropriate file is updated.
- Restart Keycloak server to take the effect of newly deployed JAR.
Form Action Configuration
This is the final configuration and it is pretty easy. Login to Keycloak admin website and select the appropriate Realm for which you wish to apply the FormAction.
- Click Authentication menu from the left sidebar.
- In the Flows tab, select the Appropriate Flow for which the action has to be configured. The multiple AuthTypes will be listed in the grid below the flows.
- From the context menu “Actions” of Registration Form, select “Add Execution”.
- You should now see a dropdown with a list of providers. Select the Signup form action that we just developed and save the configuration.
Hurray!!! we are all done. Your Sign up form should be able to validate custom fields and display appropriate error messages. The source code for custom form action is available at – https://github.com/carbonrider/keycloak_form_action
2 replies on “Keycloak custom fields validation”
Hey I am trying this for keycloak 7.0.0. I did everything that you said here but I don’t see the context menu you are talking about in the Form Action Configuration section.
Hi I am the exception . after configuring everything. Do I need to render buildpage?
Any help is appreciated.
Thanks
Rajeev
keycloak_1 | 19:46:25,132 WARN [org.keycloak.events] (default task-7) type=REGISTER_ERROR, realmId=test, clientId=frontrow, userId=null, ipAddress=192.168.160.1, error=invalid_user_credentials, auth_method=openid-connect, auth_type=code, redirect_uri=http://localhost:3000/secured, code_id=c9263faa-6883-4cf1-b58c-4f5c0fe66676, authSessionParentId=c9263faa-6883-4cf1-b58c-4f5c0fe66676, authSessionTabId=lKWSrx0Ylrc
keycloak_1 | 19:46:40,455 WARN [org.keycloak.services] (default task-7) KC-SERVICES0013: Failed authentication: java.lang.NullPointerException
keycloak_1 | at org.keycloak.keycloak-services@8.0.1//org.keycloak.authentication.FormAuthenticationFlow.renderForm(FormAuthenticationFlow.java:297)
keycloak_1 | at org.keycloak.keycloak-services@8.0.1//org.keycloak.authentication.FormAuthenticationFlow.processFlow(FormAuthenticationFlow.java:278)
keycloak_1 | at org.keycloak.keycloak-services@8.0.1//org.keycloak.authentication.DefaultAuthenticationFlow.processSingleFlowExecutionModel(DefaultAuthenticationFlow.java:394)
keycloak_1 | at org.keycloak.keycloak-services@8.0.1//org.keycloak.authentication.DefaultAuthenticationFlow.processFlow(DefaultAuthenticationFlow.java:305)
keycloak_1 | at org.keycloak.keycloak-services@8.0.1//org.keycloak.authentication.AuthenticationProcessor.authenticateOnly(AuthenticationProcessor.java:998)
keycloak_1 | at org.keycloak.keycloak-services@8.0.1//org.keycloak.authentication.AuthenticationProcessor.authenticate(AuthenticationProcessor.java:860)
keycloak_1 | at org.keycloak.keycloak-services@8.0.1//org.keycloak.services.resources.LoginActionsService.processFlow(LoginActionsService.java:296)
keycloak_1 | at org.keycloak.keycloak-services@8.0.1//org.keycloak.services.resources.LoginActionsService.processRegistration(LoginActionsService.java:629)
keycloak_1 | at org.keycloak.keycloak-services@8.0.1//org.keycloak.services.resources.LoginActionsService.registerRequest(LoginActionsService.java:683)
keycloak_1 | at org.keycloak.keycloak-services@8.0.1//org.keycloak.services.resources.LoginActionsService.registerPage(LoginActionsService.java:646)