Skip to content
Ashish.
All posts
Diagram illustrating Keycloak's adaptive authentication flow with conditional nodes and risk evaluation.

Adaptive Authentication and Conditional Flows in Keycloak

Explore how Keycloak uses adaptive authentication and conditional flows to implement risk-based, step-up authentication for enhanced security.

By Ashish KumarPart 4 of Keycloak Security Hardening

Adaptive Authentication and Conditional Flows

Static authentication policies are brittle. They treat a login from a trusted corporate network on a known device identically to a login attempt from an unfamiliar IP address on a new device. This binary approach forces users into friction-heavy flows unnecessarily or leaves the system vulnerable to credential stuffing. Keycloak addresses this through Adaptive Authentication, driven by Conditional Flows. This mechanism allows administrators to define authentication sequences that adapt based on runtime context, enabling step-up authentication where additional verification is only required when risk signals indicate a threat.

The Authentication Flow Engine

At the core of Keycloak’s security model is the Authentication Flow engine. Unlike simple linear checklists, Keycloak flows are Directed Acyclic Graphs (DAGs) of execution nodes. Each node represents an AuthenticationExecution—a specific step like "Username/Password," "OTP," or "WebAuthn."

The behavior of each node is defined by its execution requirement:

  • REQUIRED: Must succeed. If it fails, the entire flow fails.
  • ALTERNATIVE: One of the alternative nodes must succeed, but not all. This is used for multi-factor options (e.g., either OTP or WebAuthn).
  • DISABLED: The node is ignored.
  • CONDITIONAL: The node executes only if a specific condition evaluates to true. This is the primary mechanism for adaptive logic.

When a user initiates an authentication request, Keycloak constructs a flow instance. It does not execute all nodes immediately. Instead, it processes nodes sequentially, branching based on outcomes. If a node is CONDITIONAL, Keycloak invokes a ConditionalAuthenticator provider to evaluate a boolean result. This allows for fine-grained control over the authentication path, supporting various assurance levels including step up authentication scenarios where higher security checks are triggered dynamically based on context.

Implementing Conditional Logic

Adaptive authentication requires evaluating risk signals at runtime. Keycloak provides the ConditionalAuthenticator Service Provider Interface (SPI) to implement custom logic. A custom condition provider determines whether a specific execution node should proceed or be skipped, rather than manually controlling the flow path via challenge pages.

Consider a scenario where we want to enforce Multi-Factor Authentication (MFA) only if the login originates from a high-risk IP address. We create a custom HighRiskIpCondition.

public class HighRiskIpCondition implements ConditionalAuthenticator {
 
    @Override
    public boolean matchCondition(AuthenticationFlowContext context) {
        // 1. Check if MFA is already verified in this session
        if (isMfaVerified(context)) {
            return true;
        }
 
        // 2. Evaluate risk signal (e.g., IP reputation service)
        boolean isHighRisk = RiskService.checkIpReputation(context.getHttpRequest());
 
        // Return true if risk is high, triggering the subsequent MFA nodes
        // Return false if risk is low, skipping the MFA nodes
        return isHighRisk;
    }
    
    public String getDisplayType() {
        return "High Risk IP Condition";
    }
    
    public String getReferenceCategory() {
        return "HighRiskIpCondition";
    }
}

In this implementation, the matchCondition method acts as a gatekeeper. If the risk score is high, it returns true, allowing the flow to proceed to the next required step (the MFA challenge). If the risk is low, it returns false, effectively skipping the subsequent MFA nodes. This is the essence of adaptive authentication: the flow structure remains static, but the path taken through the graph changes based on dynamic evaluation by the condition provider.

Step-Up Authentication via ACR

Adaptive authentication also enables step up authentication, where a user is already authenticated but needs to perform a sensitive action requiring higher assurance. This is managed through the Authentication Context Class Reference (ACR).

In OpenID Connect (OIDC), the acr claim indicates the authentication context. Keycloak allows you to configure authentication flows that require specific ACR values. For example, you might have a "standard" flow with acr: basic and a "privileged" flow with acr: mfa.

When a client application requests access to a sensitive resource, it includes an acr_values parameter in the authorization request. Keycloak’s AuthorizationEndpoint evaluates this request against the configured flows.

GET /realms/myrealm/protocol/openid-connect/auth?
    response_type=code&
    client_id=myapp&
    acr_values=mfa

If the user’s current session has only acr: basic, Keycloak detects the mismatch. Instead of failing the request, it triggers a step-up flow. It redirects the user to an authentication page that enforces the higher ACR requirement (e.g., forcing MFA). Once the user completes this additional step, Keycloak updates the session’s ACR value and proceeds with the original request.

This mechanism ensures that users are not forced to re-authenticate from scratch for every sensitive action. Instead, they are asked only for the additional proof needed to elevate their assurance level.

Configuring Conditional Flows in the Admin Console

While custom condition providers provide the logic, the admin console defines the structure. To implement adaptive authentication:

  1. Create a New Flow: Navigate to Authentication > Flows. Create a flow named "Adaptive Login."
  2. Add Bindings: Bind this flow to the "Browser" flow type. This ensures it is used for web-based logins.
  3. Insert Conditional Nodes: In the flow definition, add your custom HighRiskIpCondition as a CONDITIONAL execution. Place it after the username/password step.
  4. Add Alternatives: Add an OTP Form and WebAuthn Authenticator as ALTERNATIVE executions following the conditional node. This means if the conditional node evaluates to true, the user can choose either OTP or WebAuthn.
  5. Set Requirements: Ensure the conditional node is set to CONDITIONAL and not REQUIRED. This prevents the flow from failing if the custom condition determines no MFA is needed based on the risk based logic.

This configuration creates a flow that always asks for a password, but conditionally asks for MFA based on the backend logic in the custom condition provider.

Trade-offs and Considerations

Implementing adaptive authentication introduces complexity. The primary trade-off is between security and user experience in risk based authentication scenarios. If the risk evaluation logic is too aggressive, legitimate users will be frequently challenged, leading to "alert fatigue" and support tickets. If it is too lenient, attackers may bypass checks.

Furthermore, custom condition providers require maintenance. Changes in threat intelligence APIs or UI updates necessitate code changes and redeployment. Keycloak’s modular architecture mitigates some of this by allowing custom authenticator JARs to be deployed independently of the core codebase, but the logic itself remains a business asset that must be rigorously tested.

Finally, client applications must be aware of ACR values. If an app does not request or validate acr_values, the step up authentication feature is invisible to the backend, reducing its effectiveness. Integration requires coordination between the identity provider and the relying party.

Conclusion

Keycloak’s conditional flows and adaptive authentication capabilities transform security from a static gate into a dynamic shield. By leveraging the DAG-based flow engine, custom SPIs, and ACR-based step-up mechanisms, identity engineers can implement risk based authentication that protects sensitive assets without imposing unnecessary friction on low-risk scenarios. This approach aligns security controls with actual threat landscapes, providing a strong foundation for modern identity management.

Related posts