# 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**

Navigate to:

```
http://<your-keycloak-domain>/admin/
```

Choose the realm where you want to enable 2FA.

#### **Enable OTP in Authentication Flow**

1. <span class="s1">Go to </span>**Authentication &gt; Flows**
2. Select the <span class="s1">**Browser**</span> flow (or copy it if you want a custom flow)
3. Locate the <span class="s1">**Browser**</span> execution list:
    
    
    - Ensure that <span class="s1">**OTP Form**</span> is listed and set to <span class="s1">**REQUIRED**</span>
    - If it’s not listed:
        
        
        - <span class="s1">Click </span>**Add Execution**
        - Choose <span class="s1">**OTP Form**</span>, then set its requirement to <span class="s1">**REQUIRED**</span>
4. Click <span class="s1">**Save**</span>

[![image.png](https://docs.elest.io/uploads/images/gallery/2025-06/scaled-1680-/NfCimage.png)](https://docs.elest.io/uploads/images/gallery/2025-06/NfCimage.png)

#### **Configure OTP Policy**

Go to <span class="s2">**Realm Settings &gt; OTP**</span> and configure:

- <span class="s1">**OTP Type**</span>: <span class="s2">TOTP</span> (time-based, most common)
- <span class="s1">**Period**</span>: 30 seconds (default)
- **Digits**<span class="s1">: 6</span>
- **Algorithm**<span class="s1">: SHA1</span>
- **Look Ahead Window**<span class="s1">: 1 or 2</span>

Click <span class="s2">**Save**</span>

[![image.png](https://docs.elest.io/uploads/images/gallery/2025-06/scaled-1680-/e6Ximage.png)](https://docs.elest.io/uploads/images/gallery/2025-06/e6Ximage.png)

## **Enforcing 2FA for Specific Users**

2FA is optional by default. To make it required for a specific user:

1. <span class="s1">Go to </span>**Users &gt; \[username\]**
2. Open the <span class="s1">**Credentials**</span> tab
3. <span class="s1">Click </span>**Set Up Required Action**
4. Choose <span class="s1">**Configure OTP**</span> from the dropdown
5. Click <span class="s1">**Save**</span>

The user will be prompted to set up 2FA on their next login.

[![image.png](https://docs.elest.io/uploads/images/gallery/2025-06/scaled-1680-/VIvimage.png)](https://docs.elest.io/uploads/images/gallery/2025-06/VIvimage.png)

## **Enforcing 2FA for All Users**

To enforce 2FA globally:

1. <span class="s1">Go to </span>**Authentication &gt; Bindings**
2. Set <span class="s1">**Browser Flow**</span> to a flow where <span class="s1">**OTP Form**</span> is <span class="s2">REQUIRED</span>
3. All users will be required to configure 2FA on their next login if not already done

[![image.png](https://docs.elest.io/uploads/images/gallery/2025-06/scaled-1680-/ssQimage.png)](https://docs.elest.io/uploads/images/gallery/2025-06/ssQimage.png)

## **Enabling 2FA via REST API**

**Get Admin Access Token**

```bash
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**

```bash
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:

```bash
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**

```bash
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 <span class="s1">manage-users</span> role
- REST API calls must use a token with <span class="s1">manage-users</span> 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 <span class="s2">otp.ftl</span> 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 <span class="s3">**conditional executions**</span> (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**

<table border="1" id="bkmrk-issue-possible-cause" style="border-collapse: collapse; border-color: rgb(0, 0, 0);"><thead><tr><th style="border-color: rgb(0, 0, 0);">**Issue**

</th><th style="border-color: rgb(0, 0, 0);">**Possible Cause**

</th><th style="border-color: rgb(0, 0, 0);">**Solution**

</th></tr></thead><tbody><tr><td style="border-color: rgb(0, 0, 0);">Users not prompted for 2FA

</td><td style="border-color: rgb(0, 0, 0);">OTP Form not set to REQUIRED in flow

</td><td style="border-color: rgb(0, 0, 0);">Set requirement to <span class="s1">REQUIRED</span> in the Browser flow

</td></tr><tr><td style="border-color: rgb(0, 0, 0);">OTP setup skips

</td><td style="border-color: rgb(0, 0, 0);"><span class="s1">Configure OTP</span> not added as required action

</td><td style="border-color: rgb(0, 0, 0);">Manually assign it to users or enforce via default flow

</td></tr><tr><td style="border-color: rgb(0, 0, 0);">“Invalid TOTP” error on login

</td><td style="border-color: rgb(0, 0, 0);">Wrong time sync or wrong app

</td><td style="border-color: rgb(0, 0, 0);">Ensure mobile device clock is correct and app supports TOTP

</td></tr><tr><td style="border-color: rgb(0, 0, 0);">OTP works once then fails

</td><td style="border-color: rgb(0, 0, 0);">Look-ahead window too small

</td><td style="border-color: rgb(0, 0, 0);">Increase look-ahead window under Realm Settings &gt; OTP

</td></tr><tr><td style="border-color: rgb(0, 0, 0);">No OTP page shown after password

</td><td style="border-color: rgb(0, 0, 0);">Flow misconfigured

</td><td style="border-color: rgb(0, 0, 0);">Review order and requirement levels of all executions in the flow

</td></tr></tbody></table>