Keycloak provides powerful mechanisms for implementing custom authentication flows through action sets and authentication sessions. This flexibility allows you to handle simple two-step authentication processes as well as complex multi-step flows, such as Passwordless, Multi-Factor Authentication (MFA), and OTP-based authentication. Let’s dive into how you can achieve these flows using action sets, discuss best practices, and explore some advanced strategies.
What are Action Sets in Keycloak?
Action sets are used to define steps in an authentication flow. Each step in the flow can be assigned specific actions, such as verifying user credentials, sending an OTP, or checking device trust. Keycloak chains these actions together to build custom flows.
Key Concepts:
- Action: A single step in the authentication process (e.g., password validation, OTP validation).
- Action Set: A collection of actions grouped as part of an authentication step.
- Execution Flow: Defines how steps and actions are executed in sequence.
Setting Up Multi-Step Authentication Flows
Example: 2-Step Flow (Password + OTP)
Step 1: Validate Password
- Action:
PasswordForm
- This form accepts the user’s username and password for validation.
- Execution Requirement: Required
Step 2: Validate OTP
- Action:
OTPForm
- This step generates and validates an OTP sent to the user’s registered email or phone.
- Execution Requirement: Required
{
"alias": "Password + OTP",
"authenticationExecutions": [
{
"authenticator": "password-form",
"requirement": "REQUIRED",
"priority": 10
},
{
"authenticator": "otp-form",
"requirement": "REQUIRED",
"priority": 20
}
]
}
Example: 3-Step Flow (Passwordless Login with Device Trust)
Step 1: Username/Email Input
- Action:
UserNameForm
- Allows the user to input their username or email.
- Execution Requirement: Required
Step 2: OTP Validation
- Action:
OTPForm
- Generates and validates an OTP for passwordless login.
- Execution Requirement: Required
Step 3: Device Trust Validation
- Action:
DeviceTrustCheck
- Checks whether the login is from a trusted device or browser.
- Execution Requirement: Conditional (skipped for trusted devices)
{
"alias": "Passwordless with Device Trust",
"authenticationExecutions": [
{
"authenticator": "username-form",
"requirement": "REQUIRED",
"priority": 10
},
{
"authenticator": "otp-form",
"requirement": "REQUIRED",
"priority": 20
},
{
"authenticator": "device-trust-check",
"requirement": "CONDITIONAL",
"priority": 30
}
]
}
Best Practices for Multi-Step Flows
-
Keep it User-Friendly
- Minimize the number of steps unless necessary.
- Use conditional actions to skip unnecessary steps (e.g., skipping OTP for trusted devices).
-
Ensure Security
- Validate inputs at each step.
- Use short-lived tokens for OTPs to prevent replay attacks.
- Implement rate limiting to avoid brute-force attacks.
-
Leverage Authentication Sessions
- Use the
AuthenticationSessionModel
to persist state across steps. - Store temporary data like OTP codes or device trust flags in the session to avoid re-fetching from the database.
- Use the
-
Error Handling
- Return descriptive error messages without revealing sensitive information.
- Log errors using Keycloak’s event system.
Code Samples
Persisting State Across Steps
You can use AuthenticationSessionModel
to store and retrieve state:
public class OTPAuthenticator implements Authenticator {
@Override
public void authenticate(AuthenticationFlowContext context) {
String otp = generateOtp();
AuthenticationSessionModel session = context.getAuthenticationSession();
session.setAuthNote("otp", otp);
sendOtpToUser(context.getUser(), otp);
context.challenge(context.form().createForm("otp-form.ftl"));
}
@Override
public void action(AuthenticationFlowContext context) {
String inputOtp = context.getHttpRequest().getDecodedFormParameters().getFirst("otp");
String sessionOtp = context.getAuthenticationSession().getAuthNote("otp");
if (inputOtp != null && inputOtp.equals(sessionOtp)) {
context.success();
} else {
context.failure(AuthenticationFlowError.INVALID_CREDENTIALS);
}
}
}
Conditional Flows
Implement conditions to skip steps dynamically:
pup)l != null;
}
}
When to Use Authentication Sessions
Scenarios:
-
Multi-Step Authentication
- Store intermediate data like OTPs, temporary user information, or device trust flags.
-
Custom State Management
- Use
AuthNotes
for short-lived state (e.g., per-login session data). - Use user attributes for longer-lived data (e.g., device trust information).
- Use
Best Practices:
- Avoid Sensitive Data in Sessions: Do not store passwords or sensitive tokens in session attributes.
- Clean Up: Remove session attributes once they are no longer needed to prevent state leakage.
- Log Events: Track state changes and authentication events using Keycloak’s event logging.
Keycloak’s flexibility with action sets and authentication sessions enables you to design secure, user-friendly, and highly customizable authentication flows. By leveraging these tools effectively, you can handle diverse scenarios, from simple password authentication to sophisticated passwordless and multi-factor workflows.