1. OTP Authentication — TOTP and HOTP
OTP (One-Time Password) is the most popular MFA method. Keycloak supports two types:
| Type | Description | Algorithm |
|---|---|---|
| TOTP (Time-based) | OTP code changes over time (every 30s) | RFC 6238 |
| HOTP (HMAC-based) | OTP code changes according to counter | RFC 4226 |
TOTP is recommended for more security — the code expires over time. HOTP is only used when the device does not support accurate clocks.
1.1 OTP Policy Configuration
Configure OTP Policy at Authentication → Policies → OTP Policy:
| Setting | Description | Default value | Recommended |
|---|---|---|---|
| OTP Type | TOTP or HOTP | totp | totp |
| OTP Hash Algorithm | Hash Algorithm | SHA1 | SHA256 or SHA512 |
| Number of Digits | Number of OTP digits | 6 | 6 (best compatible) |
| Look Ahead Window | Number of acceptable pre/post codes | 1 | 1 — increased if the user is prone to time lag |
| OTP Token Period | Time to live per token (seconds) — only TOTP | 30 | 30 |
| Initial Counter | Initial Counter — only HOTP | 0 | 0 |
| Supported Applications | Apps shown in setup instructions | FreeOTP, Google Authenticator | Add more apps if needed |
1.2 Setup OTP with Google Authenticator / FreeOTP
Step 1: Turn on OTP in Authentication Flow
By default, Browser Flow has Browser - Conditional OTP sub-flow. OTP is only required when the user has set up OTP credential. To force all users must set up OTP:
- Go to Authentication → Required Actions
- Find Configure OTP
- On Default Action: On — all new users must set up OTP
- Or turn on Required in column "Set as default action" to enforce for existing users
Step 2: User registers OTP
When user logs in for the first time (or after admin turns on Required Action):
- Keycloak displays page "Mobile Authenticator Setup"
- User opens Google Authenticator or FreeOTP app
- Scan QR code or enter manual key
- Enter the confirmation OTP code from app
- OTP credential saved for user
Step 3: Authenticate OTP
From the next login, after entering username/password, the user must enter the OTP code from the app.
1.3 Manage OTP Credentials
# Admin xem OTP credentials của user
curl -s -X GET \
"https://keycloak.example.com/admin/realms/myrealm/users/$USER_ID/credentials" \
-H "Authorization: Bearer $ADMIN_TOKEN" | \
jq '.[] | select(.type=="otp")'
# Admin xóa OTP credential (force user setup lại)
curl -X DELETE \
"https://keycloak.example.com/admin/realms/myrealm/users/$USER_ID/credentials/$CREDENTIAL_ID" \
-H "Authorization: Bearer $ADMIN_TOKEN"
# Admin trigger Required Action cho user cụ thể
curl -X PUT \
"https://keycloak.example.com/admin/realms/myrealm/users/$USER_ID" \
-H "Authorization: Bearer $ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"requiredActions": ["CONFIGURE_TOTP"]
}'
2. Recovery Codes
Recovery Codes allow users to restore access when losing their OTP device.
2.1 Enable Recovery Codes
- Go to Authentication → Required Actions
- Find Recovery Authentication Codes (if not, need to register)
- Enable Default Action or Enabled
Enforce Recovery Codes sau OTP setup:
To force users to create recovery codes immediately after setting up OTP, configure Required Actions in the following order:
CONFIGURE_TOTP— Setup OTP firstCONFIGURE_RECOVERY_AUTHN_CODES— Generate recovery codes after
2.2 Using Recovery Codes
When user loses device OTP:
- At the OTP input screen, click "Try another way"
- Select "Recovery Code"
- Enter one of the saved recovery codes
- Each code can only be used once
- After using all the codes, need to regenerate
3. WebAuthn (FIDO2) — Security Keys
WebAuthn allows authentication using hardware security keys (YubiKey, Google Titan) or platform authenticators (Touch ID, Windows Hello).
3.1 Setup WebAuthn trong Browser Flow
Step 1: Add WebAuthn to Authentication Flow
- Duplicate Browser Flow →
Browser with WebAuthn - In the Forms sub-flow, add
WebAuthn Authenticator - Flow structure:
Browser with WebAuthn ├── Cookie (Alternative) └── Forms (Alternative) ├── Username Password Form (Required) └── WebAuthn MFA (Conditional) ├── Condition - User Configured (Required) └── WebAuthn Authenticator (Required) - Bind flow: Authentication → Bindings → Browser Flow = Browser with WebAuthn
Step 2: Turn on Required Action
- Go to Authentication → Required Actions
- Find WebAuthn Register → enable Default Action
- New users will be asked to register security key
3.2 WebAuthn Policy Configuration
Configure at Authentication → Policies → WebAuthn Policy:
| Setting | Description | Value |
|---|---|---|
| Relying Party Entity Name | Display name for user | Keycloak or company name |
| Signature Algorithms | Signature Algorithm | ES256 (recommended), RS256 |
| Relying Party ID | Keycloak's domain | keycloak.example.com (or leave blank = auto) |
| Atestation Conveyance Preference | Attestation request from key | not specified or direct |
| Authenticator Attachment | Authenticator type | not specified (accepts both USB key and platform) |
| Require Resident Key | Key must be stored on device | not specified |
| User Verification Requirement | Requires user authentication on device | not specified or required |
| Create Timeout | Timeout when registering key (seconds) | 0 (no timeout) |
| Avoid Same Authenticator Register | Do not allow registering the same key twice | Off |
| Acceptable AAGUIDs | Whitelist security key models | Leave blank = accept all |
3.3 Managing WebAuthn Credentials
# Xem WebAuthn credentials của user
curl -s -X GET \
"https://keycloak.example.com/admin/realms/myrealm/users/$USER_ID/credentials" \
-H "Authorization: Bearer $ADMIN_TOKEN" | \
jq '.[] | select(.type=="webauthn")'
# Response
{
"id": "credential-uuid",
"type": "webauthn",
"userLabel": "YubiKey 5",
"createdDate": 1711900000000,
"credentialData": "{\"aaguid\":\"...\",\"credentialPublicKey\":\"...\"}"
}
Users can also manage security keys themselves in Account Console at /realms/myrealm/account/#/security/webauthn.
4. Passkeys
Passkeys is the next evolution of WebAuthn — allowing passwordless login (passwordless) using fingerprint, face recognition, or device PIN.
4.1 Passkeys vs Traditional WebAuthn
| Features | WebAuthn (MFA) | Passkeys (Passwordless) |
|---|---|---|
| Purpose | MFA — add step after password | Complete password replacement |
| Authenticator | WebAuthn Authenticator | WebAuthn Passwordless Authenticator |
| Policy | WebAuthn Policy | WebAuthn Passwordless Policy |
| Required Action | WebAuthn Register | WebAuthn Register Passwordless |
| Resident Key | Optional | Discoverable credential |
| User Verification | Optional | Required (biometrics/PIN) |
4.2 Enable Passkeys
Step 1: Configure WebAuthn Passwordless Policy
- Go to Authentication → Policies → WebAuthn Passwordless Policy
- Configuration:
- Relying Party Entity Name:
My Company - Signature Algorithms:
ES256 - User Verification Requirement:
required - Require Resident Key:
Yes— required for Passkeys
- Relying Party Entity Name:
Step 2: Create Passwordless Browser Flow
Passwordless Browser Flow
├── Cookie (Alternative)
└── Passwordless Login (Alternative)
├── WebAuthn Passwordless Authenticator (Alternative) → Đăng nhập bằng Passkey
└── Username Password Fallback (Alternative) → Sub-flow fallback
├── Username Password Form (Required)
└── Conditional OTP (Conditional)
├── Condition - User Configured (Required)
└── OTP Form (Required)
Step 3: Turn on Required Action
- Go to Authentication → Required Actions
- On WebAuthn Register Passwordless → Default Action: On
4.3 Passkey UI Modes — Conditional and Modal
Conditional UI (Autofill):
Passkey is automatically suggested in the username field — users just need to select and authenticate using biometrics. This is the smoothest experience.
To enable Conditional UI, put WebAuthn Passwordless Authenticator first in the flow with requirement Alternative.
Modal UI:
Browser displays a dialog asking for Passkey authentication. User must interact with the dialog. Used when you want to make the UX explicit.
4.4 Register Passkey via AIA (Application Initiated Action)
Users can register for Passkey at any time via AIA link:
# AIA URL để trigger Passkey registration
GET /realms/myrealm/protocol/openid-connect/auth?
client_id=my-app&
redirect_uri=https://myapp.example.com/callback&
response_type=code&
scope=openid&
kc_action=webauthn-register-passwordless
Skip if exists: If you want to skip registration when the user already has Passkey:
# Thêm parameter skip_if_exists
GET /realms/myrealm/protocol/openid-connect/auth?
client_id=my-app&
redirect_uri=https://myapp.example.com/callback&
response_type=code&
scope=openid&
kc_action=webauthn-register-passwordless&
kc_action_parameter=skip_if_exists
JavaScript integration:
// Sử dụng keycloak-js adapter
const keycloak = new Keycloak({
url: 'https://keycloak.example.com',
realm: 'myrealm',
clientId: 'my-app'
});
// Trigger Passkey registration
function registerPasskey() {
keycloak.login({
action: 'webauthn-register-passwordless'
});
}
// Button trong UI
document.getElementById('registerPasskeyBtn')
.addEventListener('click', registerPasskey);
5. Kerberos Authentication
Kerberos enables Single Sign-On automatically for users logged in to the domain (Windows AD, MIT Kerberos). User does not need to enter credentials — browser automatically sends Kerberos ticket.
5.1 Kerberos Server Setup
Request:
- KDC (Key Distribution Center) is running — Active Directory or MIT Kerberos
- SPN (Service Principal Number) cho Keycloak service
- Keytab file cho Keycloak
# Tạo SPN và keytab cho Keycloak (MIT Kerberos)
kadmin -q "addprinc -randkey HTTP/[email protected]"
kadmin -q "ktadd -k /etc/keycloak/keycloak.keytab HTTP/[email protected]"
# Đặt permission
chmod 600 /etc/keycloak/keycloak.keytab
chown keycloak:keycloak /etc/keycloak/keycloak.keytab
5.2 Configure Keycloak for Kerberos
Option 1: Kerberos User Storage Provider
- Go to User Federation → Add provider → Kerberos
- Configuration:
- Kerberos Realm:
EXAMPLE.COM - Server Principal:
HTTP/[email protected] - Key Tab:
/etc/keycloak/keycloak.keytab - Allow Kerberos Authentication: On
- Use Kerberos For Password Authentication: On
- Update First Login: On
- Kerberos Realm:
Option 2: LDAP + Kerberos (Active Directory)
- Go to User Federation → Add provider → LDAP
- Configure LDAP connection for AD
- Enable Allow Kerberos Authentication
- Enter Kerberos Realm, Server Principal, Key Tab
5.3 Enable Kerberos in Browser Flow
- Go to Authentication → Flows → Browser (or custom flow)
- Find Kerberos execution
- Change requirement from
DisabledtoAlternative
Browser Flow (Kerberos enabled)
├── Cookie (Alternative)
├── Kerberos (Alternative) ← BẬT lên
├── Identity Provider Redirector (Alternative)
└── Forms (Alternative)
├── Username Password Form (Required)
└── Browser - Conditional OTP (Conditional)
├── Condition - User Configured (Required)
└── OTP Form (Required)
5.4 Cross-realm Trust
Allow users from one Kerberos realm to trust another realm:
# /etc/krb5.conf trên Keycloak server
[libdefaults]
default_realm = CORP.EXAMPLE.COM
dns_lookup_realm = false
dns_lookup_kdc = false
[realms]
CORP.EXAMPLE.COM = {
kdc = dc1.corp.example.com
admin_server = dc1.corp.example.com
}
PARTNER.EXAMPLE.COM = {
kdc = kdc.partner.example.com
}
[capaths]
PARTNER.EXAMPLE.COM = {
CORP.EXAMPLE.COM = .
}
6. X.509 Client Certificate Authentication
X.509 allows authentication using client certificate — common in enterprise, government, or mTLS environments.
6.1 Add X.509 to Browser Flow
- Duplicate Browser Flow →
Browser with X.509 - Add
X509/Validate Username Formto flow
Browser with X.509
├── Cookie (Alternative)
├── X509/Validate Username Form (Alternative) ← Thêm mới
├── Identity Provider Redirector (Alternative)
└── Forms (Alternative)
├── Username Password Form (Required)
└── Browser - Conditional OTP (Conditional)
├── Condition - User Configured (Required)
└── OTP Form (Required)
6.2 X.509 Authenticator Configuration
Click ⚙️ next to X509/Validate Username Form:
| Setting | Description | Example |
|---|---|---|
| User Identity Source | Field in certificate to identify user | Subject's Common Name, Subject's e-mail |
| Mapping Source to User Attribute | Map identity source sang user attribute | Username or Email |
| A regular expression | Regex extract identity from cert field | CN=(.*?)(?:,\|$) |
| CRL Checking Enabled | Checking Certificate Revocation List | On |
| CRL Distribution Point | URL or path to CRL | ldap://ca.example.com/CN=... |
| OCSP Checking Enabled | Checking Online Certificate Status Protocol | On |
| OCSP Responder URI | OCSP responder URL | http://ocsp.example.com |
| Certificate Key Usage | Key usage required | digitalSignature |
| Certificate Extended Key Usage | Extended key usage | clientAuth |
| Certificate Policy Validation Mode | Validate certificate policies | Not Specified |
6.3 Certificate Mapping Strategies
There are many ways to map certificate to Keycloak user:
# 1. Subject's Common Name → Username
# Certificate: CN=john.doe, OU=Engineering, O=Example Corp
# → username: john.doe
# 2. Subject's e-mail → Email
# Certificate: [email protected]
# → email: [email protected]
# 3. Serial Number → User Attribute
# Certificate: Serial=1A2B3C4D
# → user attribute "x509_serial" = "1A2B3C4D"
# 4. SHA-256 Certificate Thumbprint → User Attribute
# Certificate SHA-256: ab:cd:ef:12:34:...
# → user attribute "x509_thumbprint" = "ab:cd:ef:12:34:..."
# 5. Subject's DN với regex
# Certificate: CN=john.doe, OU=Engineering, O=Example Corp, C=VN
# Regex: CN=(.*?)(?:,|$)
# → Extracted: john.doe
6.4 CRL and OCSP Checking
To ensure certificates have not been revoked:
CRL (Certificate Revocation List):
- Keycloak downloads and caches CRL from configured URI
- Check if the serial number of the client cert is in the CRL
- If CRL not available and
CRL Checking Enabled=On→ authentication fail
OCSP (Online Certificate Status Protocol):
- Real-time check certificate status
- Keycloak sends request to OCSP Responder
- Faster than CRL for individual checks
- Disadvantages: depends on OCSP server availability
# Test OCSP check
openssl ocsp \
-issuer ca.pem \
-cert client.pem \
-url http://ocsp.example.com \
-resp_text
# Test certificate info
openssl x509 -in client.pem -noout -subject -serial -fingerprint -sha256
6.5 Keycloak mTLS Setup
For Keycloak to receive client certificates, you need to configure a reverse proxy or Keycloak directly:
# Docker Compose — Keycloak với mTLS qua nginx
services:
nginx:
image: nginx:latest
ports:
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- ./certs/server.crt:/etc/nginx/certs/server.crt
- ./certs/server.key:/etc/nginx/certs/server.key
- ./certs/ca.crt:/etc/nginx/certs/ca.crt
depends_on:
- keycloak
keycloak:
image: quay.io/keycloak/keycloak:26.0
command: start --proxy-headers xforwarded
environment:
KC_HOSTNAME: keycloak.example.com
KC_HTTP_ENABLED: "true"
KC_PROXY_HEADERS: xforwarded
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: admin
# nginx.conf — Forward client certificate
server {
listen 443 ssl;
server_name keycloak.example.com;
ssl_certificate /etc/nginx/certs/server.crt;
ssl_certificate_key /etc/nginx/certs/server.key;
ssl_client_certificate /etc/nginx/certs/ca.crt;
ssl_verify_client optional;
location / {
proxy_pass http://keycloak:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port 443;
# Forward client certificate
proxy_set_header ssl-client-cert $ssl_client_escaped_cert;
}
}
7. Compare MFA
methods| Method | Security | UX | Phishing-resistant | Use case |
|---|---|---|---|---|
| TOTP/HOTP | Average | Good | No | Popular, easy to deploy |
| WebAuthn (MFA) | High | Good | Yes | Enterprise, compliance |
| Passkeys | Very High | Excellent | Yes | Consumer + Enterprise |
| Kerberos | High | Excellent (transparent) | Yes (domain) | Enterprise, Windows domain |
| X.509 Certificate | Very High | Transparent | Yes | Government, military, banking |
8. Summary
| Concept | Description |
|---|---|
| OTP Policy | TOTP/HOTP config — algorithm, digits, period, look ahead |
| Recovery Codes | Backup codes when OTP device is lost |
| WebAuthn | FIDO2 security keys — phishing-resistant MFA |
| Passkeys | Passwordless authentication — discoverable credentials + biometrics |
| Kerberos | Transparent SSO cho domain users |
| X.509 | Client certificate authentication — mTLS |
| CRL/OCSP | Certificate revocation checking |