
Writing a Keycloak Custom Authenticator
A guide to building a Keycloak custom authenticator using the Authenticator SPI for Java developers and identity engineers.
Keycloak extends its authentication capabilities not just through configuration, but through a Service Provider Interface (SPI) that allows Java code to hook directly into the authentication lifecycle. For identity engineers, understanding the Authenticator SPI is critical when built-in flows (like Username/Password or OTP) cannot satisfy specific business requirements, such as validating a custom HTTP header, checking an external identity provider, or enforcing multi-factor conditions based on IP reputation.
Part 5 of the Keycloak Themes and SPIs series details the mechanism of building a custom authenticator, focusing on the interaction between your Java code and Keycloak’s AuthenticationFlowContext.
The Authenticator SPI Contract
At the core of any custom authenticator is the org.keycloak.authentication.Authenticator interface. Keycloak’s authentication engine treats each step in a flow as an independent execution point. When a user interacts with a login page or an API endpoint, Keycloak creates an AuthenticationExecutionModel object. Your code must implement specific methods that Keycloak calls at precise moments in this lifecycle.
The most critical method is authenticate(AuthenticationFlowContext context). This is where the primary validation logic lives. The interface also requires action(AuthenticationFlowContext context), which Keycloak calls when a user submits a form or other response to a challenge issued during authenticate(). Additionally, the AuthenticationFlowContext provides a challenge(Response challenge) method you can call from within authenticate() to return HTML forms or error responses if validation fails.
When you call context.failure(), the behavior depends on the flow configuration. Typically, if no fallback is defined, the entire authentication attempt fails. If a "Failure" branch is defined, Keycloak routes the execution there. Rendering a challenge form is typically done by a subsequent execution in that branch, not automatically by the failure itself.
Implementing the Logic: A Header-Based Example
Consider a scenario where your organization requires all internal API requests to include a specific header, X-Internal-Token, for a "Direct Grant" flow. We will implement an authenticator that checks this header.
You implement the org.keycloak.authentication.Authenticator interface directly. Besides authenticate() and action(), you also provide simple implementations of the interface's other lifecycle methods (such as requiresUser(), configuredFor(), and setRequiredActions()), allowing you to focus on the specific validation logic.
package com.example.auth;
import org.keycloak.authentication.AuthenticationFlowContext;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
public class HeaderAuthenticator implements Authenticator {
private static final String HEADER_NAME = "X-Internal-Token";
@Override
public void authenticate(AuthenticationFlowContext context) {
// 1. Retrieve the HTTP request from the context
String token = context.getHttpRequest().getHttpHeaders().getFirst(HEADER_NAME);
if (token != null && token.equals("VALID_SECRET_TOKEN")) {
// 2. Mark the execution as successful
context.success();
} else {
// 3. Fail the execution. This can trigger a challenge or just fail the flow
context.failure();
}
}
@Override
public void action(AuthenticationFlowContext context) {
// Post-execution logic, if any
}
// ... requiresUser(), configuredFor(), setRequiredActions(), and close() omitted for brevity ...
}Mechanism of State Transition
The key mechanism here is context.success() and context.failure(). These methods do not just set a boolean flag; they modify the internal state of the AuthenticationSessionModel.
When you call context.success(), Keycloak marks the current AuthenticationExecutionModel as complete. The engine then looks at the flow configuration to determine the next step. If the flow has a "Success" branch pointing to the next execution, Keycloak moves there. If it is the last step, Keycloak proceeds to finalize the session.
If you call context.failure(), the behavior depends on the flow configuration. Typically, if no fallback is defined, the entire authentication attempt fails. If a "Failure" branch is defined, Keycloak routes the execution there. This is how you implement conditional logic within a single flow.
Registering the SPI
Java SPI requires a service provider configuration file. In your project’s src/main/resources/META-INF/services/, create a file named org.keycloak.authentication.AuthenticatorFactory. Inside, list the fully qualified name of your factory implementation:
com.example.auth.HeaderAuthenticatorFactory
Additionally, you need a factory class that implements AuthenticatorFactory. This factory tells Keycloak about your authenticator’s metadata, such as its ID, display name, and whether it requires configuration.
public class HeaderAuthenticatorFactory implements AuthenticatorFactory {
public static final String ID = "header-token-authenticator";
@Override
public String getId() {
return ID;
}
@Override
public Authenticator create(KeycloakSession session) {
return new HeaderAuthenticator();
}
// ... other factory methods ...
}Keycloak loads your authenticator through this factory, so only the factory class needs to be listed in the service file — the HeaderAuthenticator implementation itself is not registered via SPI directly.
Configuring in Keycloak
Once the JAR is deployed to Keycloak’s providers directory and the module is enabled, the authenticator appears in the Admin Console.
- Navigate to Authentication > Flows.
- Bind a flow (e.g., "Browser" or "Direct Grant") or create a new flow.
- Click Add Step > Add Execution.
- Select your "Header Token Validator" from the list.
- Set the Requirement to:
- Required: The flow fails if the header is missing/invalid.
- Alternative: If this fails, try another execution (useful for fallback mechanisms).
- Disabled: The step is skipped unless explicitly triggered.
Conditional Execution
For more complex scenarios, you might want the header check to only apply to certain clients. Keycloak supports "Conditional Authenticator" steps. You can add a "Condition" step before your custom authenticator. Keycloak provides built-in conditions like "User Role" or "Client Scope". If you need custom conditions, you implement the ConditionalAuthenticator SPI, which is similar to the Authenticator SPI but returns a boolean instead of transitioning flow state.
Common Pitfalls
- Infinite Loops: If your authenticator fails and the flow configuration points back to itself or creates a cycle, Keycloak will hit a recursion limit and throw an error. Always ensure your failure branches lead to a terminal state or a different execution path.
- Statelessness: Remember that each HTTP request is a new transaction. Do not store sensitive data in static variables. Use the
AuthenticationSessionModelto pass data between steps if necessary. - Thread Safety: Keycloak runs authenticators in a multithreaded environment. Ensure your
Authenticatorimplementation is thread-safe. Avoid instance variables that mutate state; rely on thecontextparameter passed to each method.
Conclusion
By implementing the Authenticator SPI, you gain fine-grained control over the authentication flow, allowing you to integrate external systems and enforce custom security policies directly within Keycloak’s flow engine. For further details on flow configuration, refer to the official Keycloak documentation on Authentication Flows.
Related posts
Keycloak Architecture Deep Dive: Internal Components and Data Flow
An examination of Keycloak architecture, internal components, SPI extensions, themes, and the core data model for advanced developers.
The Keycloak Service Provider Interface, Explained
A technical examination of the Keycloak Service Provider Interface (SPI), covering custom providers, provider factories, and extension strategies for Java developers.
Keycloak Theme Structure and the Base Theme
A technical overview of Keycloak's theme structure, focusing on the base theme, theme.properties, and inheritance mechanisms for frontend developers.