Enabling Two-Factor Authentication (2FA) in Keycloak
Two-Factor Authentication (2FA) adds an extra layer of security to user logins by requiring something the user knows (password) and something they have (typically an OTP via a mobile app). This guide explains how to enable and enforce OTP-based 2FA for all or specific users in Keycloak, using the Admin Console, authentication flows, and best practices.
Enabling 2FA via the Admin Console
Log in to the Admin Console
http://<your-keycloak-domain>/admin/
Choose the realm where you want to enable 2FA.
Enable OTP in Authentication Flow
-
Go to Authentication > Flows
-
Select the Browser flow (or copy it if you want a custom flow)
-
Locate the Browser execution list:
-
Ensure that OTP Form is listed and set to REQUIRED
-
If it’s not listed:
-
Click Add Execution
-
Choose OTP Form, then set its requirement to REQUIRED
-
-
-
Click Save
Configure OTP Policy
Go to Realm Settings > OTP and configure:
-
OTP Type: TOTP (time-based, most common)
-
Period: 30 seconds (default)
-
Digits: 6
-
Algorithm: SHA1
-
Look Ahead Window: 1 or 2
Click Save
Enforcing 2FA for Specific Users
2FA is optional by default. To make it required for a specific user:
-
Go to Users > [username]
-
Open the Credentials tab
-
Click Set Up Required Action
-
Choose Configure OTP from the dropdown
-
Click Save
The user will be prompted to set up 2FA on their next login.
Enforcing 2FA for All Users
To enforce 2FA globally:
-
Go to Authentication > Bindings
-
Set Browser Flow to a flow where OTP Form is REQUIRED
-
All users will be required to configure 2FA on their next login if not already done
Enabling 2FA via REST API
Get Admin Access Token
curl -X POST "https://<keycloak-domain>/realms/master/protocol/openid-connect/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "username=admin" \
-d "password=admin-password" \
-d "grant_type=password" \
-d "client_id=admin-cli"
Assign “Configure OTP” Required Action to a User
curl -X PUT "https://<keycloak-domain>/admin/realms/<realm>/users/<user-id>" \
-H "Authorization: Bearer <access_token>" \
-H "Content-Type: application/json" \
-d '{"requiredActions": ["CONFIGURE_TOTP"]}'
To get the user ID:
curl -H "Authorization: Bearer <access_token>" \
https://<keycloak-domain>/admin/realms/<realm>/users?username=<username>
Enabling 2FA via Docker CLI
Authenticate and Set OTP Action
docker exec -it keycloak bash
/opt/keycloak/bin/kcadm.sh config credentials \
--server http://localhost:8080 \
--realm master --user admin --password admin
/opt/keycloak/bin/kcadm.sh update users/<user-id> -r <realm> \
-s 'requiredActions=["CONFIGURE_TOTP"]'
Required Permissions for 2FA Management
-
Requires manage-users role
-
REST API calls must use a token with manage-users permission in the realm
To assign via Admin Console:
Users > [admin-user] > Role Mappings > Realm Roles > Add 'manage-users'
Best Practices for 2FA
- Use Time-Based OTP (TOTP): TOTP is compatible with standard apps like Google Authenticator, Authy, or FreeOTP.
- Customize OTP Setup Page: Modify the otp.ftl page inside your theme to reflect your brand and offer setup instructions.
- Inform Users Before Enforcing: Enable OTP as a required action with communication ahead of rollout to avoid login issues.
- Use Conditional 2FA Flows: Use conditional executions (e.g., only require OTP from outside a trusted network/IP range).
- Back Up OTP Configuration: Encourage users to back up their OTP seed or enable recovery codes for critical accounts.
Common Issues and Troubleshooting
Issue |
Possible Cause |
Solution |
---|---|---|
Users not prompted for 2FA |
OTP Form not set to REQUIRED in flow |
Set requirement to REQUIRED in the Browser flow |
OTP setup skips |
Configure OTP not added as required action |
Manually assign it to users or enforce via default flow |
“Invalid TOTP” error on login |
Wrong time sync or wrong app |
Ensure mobile device clock is correct and app supports TOTP |
OTP works once then fails |
Look-ahead window too small |
Increase look-ahead window under Realm Settings > OTP |
No OTP page shown after password |
Flow misconfigured |
Review order and requirement levels of all executions in the flow |
No comments to display
No comments to display