1. User Federation — Overview
User Federation allows Keycloak to connect to external user databases such as LDAP, Active Directory, or custom databases. Instead of having to import all users into Keycloak, you can authenticate directly from an external source.
To configure User Federation, go to Admin Console → User Federation.
1.1 Why do we need User Federation?
| Reason | Explanation |
|---|---|
| Centralize user management | LDAP/AD is already the main source of users in the enterprise → no need to duplicate |
| Keep the current system intact | No need to migrate users to Keycloak |
| Single Source of Truth | User data only exists in one place, avoid inconsistency |
| Kerberos SSO | Integrating Kerberos authentication from Active Directory |
1.2 Types of Federation Provider
| Provider | Description |
|---|---|
| LDAP | Supports OpenLDAP, 389 Directory Server, and LDAP-compliant servers |
| Active Directory | Microsoft Active Directory (using LDAP protocol + AD-specific mappers) |
| SSSD | System Security Services Daemon — FreeIPA/Red Hat IdM integration |
| Custom User Storage SPI | Write your own provider to connect any database |
2. Add LDAP Provider
Go to Admin Console → User Federation → Add LDAP providers.
2.1 General Options
| Setting | Description | Sample value |
|---|---|---|
| Console display name | Display name on Admin Console | Corporate LDAP |
| Priority | Priority order when there are many providers | 0 (highest) |
| Enabled | Enable/disable provider | ON |
| Import users | Import LDAP users into Keycloak local database | ON |
2.2 Connection Settings
# Connection URL
Connection URL: ldap://ldap.example.com:389
# Hoặc LDAPS (SSL):
Connection URL: ldaps://ldap.example.com:636
# Bind Type
Bind Type: simple
# Bind DN — tài khoản để Keycloak kết nối LDAP
Bind DN: cn=admin,dc=example,dc=com
# Bind Credential — mật khẩu
Bind Credential: ********
Connection Pool Settings:
| Setting | Description | Default |
|---|---|---|
| Connection pooling | Turn on connection pooling to optimize performance | ON |
| Connection pool authentication | Pool cho authenticated connections | simple |
| Connection pool debug | Log debug cho connection pool | OFF |
| Connection pool initial size | Number of initial initial connections | 1 |
| Connection pool maximum size | Maximum number of connections | 1000 |
| Connection pool timeout | Time to wait for connection from pool | 30000 ms |
2.3 SSL/LDAPS Configuration
To connect to LDAPS (port 636), you need to import CA certificate into Keycloak truststore:
# Tải CA certificate từ LDAP server
openssl s_client -connect ldap.example.com:636 -showcerts < /dev/null 2>/dev/null | \
openssl x509 -outform PEM > ldap-ca.pem
# Import vào Java truststore
keytool -import -alias ldap-ca \
-keystore /opt/keycloak/conf/truststore.jks \
-file ldap-ca.pem \
-storepass changeit -noprompt
# Hoặc sử dụng PEM truststore (Keycloak 24+)
# Đặt file PEM vào /opt/keycloak/conf/truststores/
cp ldap-ca.pem /opt/keycloak/conf/truststores/
Configure Keycloak using truststore:
# keycloak.conf
# Java keystore
spi-truststore-file-file=/opt/keycloak/conf/truststore.jks
spi-truststore-file-password=changeit
spi-truststore-file-type=JKS
# Hoặc PEM directory (Keycloak 24+)
truststore-paths=/opt/keycloak/conf/truststores
2.4 Use StartTLS
Instead of LDAPS (port 636), you can use StartTLS on port 389:
Connection URL: ldap://ldap.example.com:389
Use StartTLS: ON
StartTLS upgrade regular LDAP connection to encrypted connection on same port 389.
3. LDAP Searching Settings
| Setting | Description | Sample value |
|---|---|---|
| Users DN | Base DN where Keycloak looks for users | ou=People,dc=example,dc=com |
| User Object Classes | LDAP object class cho user entries | inetOrgPerson, organizationalPerson |
| Username LDAP attribute | LDAP attribute contains username | uid (LDAP) / sAMAccountName (AD) |
| RDN LDAP attribute | Attribute used for RDN (Relative Distinguished Name) | uid (LDAP) / cn (AD) |
| UUID LDAP attribute | Attribute used as unique ID | entryUUID (LDAP) / objectGUID (AD) |
| Search Scope | One Level or Subtree | Subtree |
| Custom User LDAP Filter | LDAP additional filter to filter users | (&(objectClass=person)(memberOf=cn=app-users,ou=Groups,dc=example,dc=com)) |
| Read Timeout | Timeout cho LDAP read operations | 30000 ms |
3.1 Active Directory Settings
When selecting Vendor = Active Directory, Keycloak automatically configures the appropriate values:
Username LDAP attribute: cn
RDN LDAP attribute: cn
UUID LDAP attribute: objectGUID
User Object Classes: person, organizationalPerson, user
Users DN: cn=Users,dc=corp,dc=example,dc=com
4. Storage Modes
Keycloak supports 3 storage modes that dictate how Keycloak interacts with LDAP:
| Mode | Read from LDAP | Write back to LDAP | Import to Keycloak DB | Use case |
|---|---|---|---|---|
| READ_ONLY | ✅ | ❌ | ✅ (cache) | LDAP is the only source, does not allow users to change information via Keycloak |
| WRITABLE | ✅ | ✅ | ✅ | Allow users to change information (password, profile) and write back to LDAP |
| UNSYNCED | ✅ | ❌ | ✅ | Import users from LDAP, then save changes only in Keycloak DB (no writeback) |
4.1 Edit Modes
Edit Mode regulates the behavior when a user or admin changes information:
READ_ONLY:
- User không thể đổi password qua Keycloak
- Admin không thể edit user attributes
- Mọi thay đổi phải thực hiện trực tiếp trên LDAP
WRITABLE:
- User có thể đổi password → Keycloak ghi ngược về LDAP
- Admin edit user attributes → cập nhật LDAP
- Cẩn thận với password policy: phải match giữa Keycloak và LDAP
UNSYNCED:
- User đổi password → chỉ lưu trong Keycloak DB
- Đăng nhập: Keycloak thử password local trước, nếu fail thì thử LDAP
- Phù hợp khi muốn dần migrate users sang Keycloak
5. Sync Settings
Keycloak can synchronize users from LDAP by 2 mechanisms:
5.1 Periodic Full Sync
# Import toàn bộ users từ LDAP vào Keycloak DB
Periodic Full Sync: ON
Full Sync Period: 604800 # seconds (7 ngày)
5.2 Periodic Changed Users Sync
# Chỉ đồng bộ users có thay đổi (dựa vào modifyTimestamp)
Periodic Changed Users Sync: ON
Changed Users Sync Period: 86400 # seconds (1 ngày)
5.3 Manual Sync
You can trigger sync manually from Admin Console or via CLI:
# Trigger full sync qua Admin REST API
curl -X POST "http://localhost:8080/admin/realms/my-realm/user-storage/${LDAP_PROVIDER_ID}/sync?action=triggerFullSync" \
-H "Authorization: Bearer ${ACCESS_TOKEN}"
# Trigger changed users sync
curl -X POST "http://localhost:8080/admin/realms/my-realm/user-storage/${LDAP_PROVIDER_ID}/sync?action=triggerChangedUsersSync" \
-H "Authorization: Bearer ${ACCESS_TOKEN}"
6. LDAP Mappers
LDAP Mappers defines how Keycloak map LDAP attributes to Keycloak user model. This is the most important part when configuring LDAP federation.
6.1 user-attribute-ldap-mapper
Map an LDAP attribute to a Keycloak user attribute:
Mapper Type: user-attribute-ldap-mapper
LDAP Attribute: mail
User Model Attribute: email
Read Only: true
Always Read Value From LDAP: false
Is Mandatory In LDAP: true
Automatically generated default mappers:
| Mapper Name | LDAP Attribute | Keycloak Attribute |
|---|---|---|
| username | uid / sAMAccountName | username |
mail | email | |
| first name | givenName / cn | firstName |
| last name | sn | lastName |
| creation date | createTimestamp | createTimestamp |
| modify date | modifyTimestamp | modifyTimestamp |
6.2 full-name-ldap-mapper
Map LDAP cn (Common Name) sang Keycloak firstName + lastName:
Mapper Type: full-name-ldap-mapper
LDAP Full Name Attribute: cn
Read Only: true
Write Only: false
Useful when LDAP only has cn without separating givenName/sn.
6.3 group-ldap-mapper
Sync LDAP groups to Keycloak groups:
Mapper Type: group-ldap-mapper
LDAP Groups DN: ou=Groups,dc=example,dc=com
Group Name LDAP Attribute: cn
Group Object Classes: groupOfNames
Membership LDAP Attribute: member
Membership Attribute Type: DN
Membership User LDAP Attribute: uid
Mode: READ_ONLY
User Groups Retrieve Strategy: LOAD_GROUPS_BY_MEMBER_ATTRIBUTE
Drop non-existing groups during sync: false
Groups Path: /
User Groups Retrieve Strategy options:
| Strategy | Description |
|---|---|
| LOAD_GROUPS_BY_MEMBER_ATTRIBUTE | Load groups from LDAP based on member attribute |
| GET_GROUPS_FROM_USER_MEMBEROF_ATTRIBUTE | Read memberOf attribute on user entry |
| LOAD_GROUPS_BY_MEMBER_ATTRIBUTE_RECURSIVELY | Load groups recursively (nested groups) |
6.4 role-ldap-mapper
Sync LDAP roles/groups to Keycloak realm roles:
Mapper Type: role-ldap-mapper
LDAP Roles DN: ou=Roles,dc=example,dc=com
Role Name LDAP Attribute: cn
Role Object Classes: groupOfNames
Membership LDAP Attribute: member
Membership Attribute Type: DN
Membership User LDAP Attribute: uid
Mode: READ_ONLY
Use Realm Roles Mapping: true
Client ID: (để trống nếu dùng realm roles)
6.5 hardcoded-ldap-role-mapper
Automatically assign a fixed role to all users from LDAP provider:
Mapper Type: hardcoded-ldap-role-mapper
Role: realm-role-name
# Hoặc client role:
Role: client-id.client-role-name
Useful when you want to distinguish users from LDAP from local users with a role marker.
6.6 msad-user-account-control-mapper
Special Mapper for Active Directory, handling userAccountControl attribute:
Mapper Type: msad-user-account-control-mapper
# Xử lý:
# - Account enabled/disabled status
# - Password expired status
# - Account locked status
# - Require user to change password at next login
This Mapper reads bitmask userAccountControl of AD to map to Keycloak user status:
| AD Flag (bit) | Keycloak Behavior |
|---|---|
ACCOUNTDISABLE (0x0002) | User is disabled in Keycloak |
LOCKOUT (0x0010) | User is locked |
PASSWORD_EXPIRED | User must change password when logging in |
6.7 certificate-ldap-mapper
Map LDAP certificate attribute sang Keycloak user attribute cho X.509 authentication:
Mapper Type: certificate-ldap-mapper
LDAP Attribute: userCertificate
User Model Attribute: usercertificate
Is DER Formatted: true
Always Read Value From LDAP: true
7. Password Hashing
When using LDAP federation, password hashing has several important characteristics:
| Scenario | Password Hash | Note |
|---|---|---|
| READ_ONLY mode | Password always verifies directly with LDAP server | Keycloak does not save password hash |
| WRITABLE mode | Password is recorded to LDAP according to LDAP password policy | LDAP server performs hashing |
| UNSYNCED mode | New password stored in Keycloak DB with Keycloak hashing | Old password still verified via LDAP |
# Kiểm tra password policy trên LDAP (OpenLDAP)
ldapsearch -x -H ldap://localhost:389 \
-D "cn=admin,dc=example,dc=com" -W \
-b "cn=config" "(objectClass=olcGlobal)" olcPasswordHash
# Output ví dụ:
# olcPasswordHash: {SSHA}
8. SSSD and FreeIPA Integration
Keycloak supports integration with SSSD (System Security Services Daemon) via the D-Bus interface, allowing authenticate users from FreeIPA or Red Hat Identity Manager.
8.1 Prerequisites
# Cài đặt SSSD trên Keycloak server
sudo dnf install sssd sssd-dbus
# Cấu hình SSSD (/etc/sssd/sssd.conf)
[sssd]
services = nss, pam, ifp
domains = example.com
[domain/example.com]
id_provider = ipa
auth_provider = ipa
access_provider = ipa
ipa_domain = example.com
ipa_server = ipa.example.com
[ifp]
allowed_uids = root, keycloak
user_attributes = +mail, +givenname, +sn, +telephoneNumber
8.2 Configure SSSD Federation Provider
In Admin Console, add SSSD federation provider — Keycloak will communicate with SSSD via D-Bus to:
- Authenticate users (PAM)
- Get user attributes (InfoPipe)
- Get group membership
9. Kerberos Bridge
Keycloak can use Kerberos authentication in conjunction with LDAP federation, allowing users to log in automatically using a Kerberos ticket (SPNEGO).
9.1 Configuring Kerberos with LDAP
# Trong LDAP provider settings
Allow Kerberos authentication: ON
Kerberos Realm: EXAMPLE.COM
Server Principal: HTTP/[email protected]
KeyTab: /etc/keycloak/keycloak.keytab
Use Kerberos for password authentication: ON
# Tạo keytab cho Keycloak service principal
kadmin.local -q "addprinc -randkey HTTP/[email protected]"
kadmin.local -q "ktadd -k /etc/keycloak/keycloak.keytab HTTP/[email protected]"
# Set permissions
chown keycloak:keycloak /etc/keycloak/keycloak.keytab
chmod 600 /etc/keycloak/keycloak.keytab
9.2 Browser Configuration cho SPNEGO
Firefox:
1. about:config
2. network.negotiate-auth.trusted-uris = .example.com
3. network.negotiate-auth.delegation-uris = .example.com
Chrome / Edge:
1. Policy: AuthServerAllowlist = *.example.com
2. Hoặc command line: --auth-server-whitelist="*.example.com"
10. Custom User Storage SPI
When LDAP is not enough, you can write Custom User Storage Provider to connect any data source (SQL database, REST API, legacy system...).
10.1 SPI Interfaces
// UserStorageProviderFactory — tạo provider instances
public class MyUserStorageProviderFactory
implements UserStorageProviderFactory<MyUserStorageProvider> {
@Override
public String getId() {
return "my-user-storage";
}
@Override
public MyUserStorageProvider create(KeycloakSession session,
ComponentModel model) {
return new MyUserStorageProvider(session, model);
}
}
// UserStorageProvider — implement các interfaces cần thiết
public class MyUserStorageProvider implements
UserStorageProvider,
UserLookupProvider,
CredentialInputValidator,
UserQueryProvider {
@Override
public UserModel getUserByUsername(RealmModel realm, String username) {
// Query external database
ExternalUser extUser = externalDb.findByUsername(username);
if (extUser == null) return null;
// Wrap vào Keycloak UserModel
return new UserAdapter(session, realm, model, extUser);
}
@Override
public boolean isValid(RealmModel realm, UserModel user,
CredentialInput input) {
if (!supportsCredentialType(input.getType())) return false;
// Verify password với external system
return externalDb.verifyPassword(
user.getUsername(),
input.getChallengeResponse()
);
}
}
10.2 Deploy Custom Provider
# Build JAR
mvn clean package
# Copy vào Keycloak providers directory
cp target/my-user-storage.jar /opt/keycloak/providers/
# Rebuild Keycloak
/opt/keycloak/bin/kc.sh build
11. Configure LDAP with kcadm.sh
Use kcadm.sh to configure LDAP federation via command line:
# Đăng nhập
kcadm.sh config credentials \
--server http://localhost:8080 \
--realm master \
--user admin \
--password admin
# Tạo LDAP provider
kcadm.sh create components -r my-realm \
-s name="Corporate LDAP" \
-s providerId=ldap \
-s providerType=org.keycloak.storage.UserStorageProvider \
-s 'config.vendor=["other"]' \
-s 'config.connectionUrl=["ldap://ldap.example.com:389"]' \
-s 'config.bindDn=["cn=admin,dc=example,dc=com"]' \
-s 'config.bindCredential=["admin_password"]' \
-s 'config.usersDn=["ou=People,dc=example,dc=com"]' \
-s 'config.userObjectClasses=["inetOrgPerson, organizationalPerson"]' \
-s 'config.usernameLDAPAttribute=["uid"]' \
-s 'config.rdnLDAPAttribute=["uid"]' \
-s 'config.uuidLDAPAttribute=["entryUUID"]' \
-s 'config.editMode=["READ_ONLY"]' \
-s 'config.syncRegistrations=["false"]' \
-s 'config.searchScope=["2"]' \
-s 'config.importEnabled=["true"]' \
-s 'config.enabled=["true"]' \
-s 'config.priority=["0"]' \
-s 'config.fullSyncPeriod=["604800"]' \
-s 'config.changedSyncPeriod=["86400"]'
# Lấy LDAP provider ID
LDAP_ID=$(kcadm.sh get components -r my-realm \
--fields id,name \
-q providerType=org.keycloak.storage.UserStorageProvider \
| jq -r '.[0].id')
# Thêm group mapper
kcadm.sh create components -r my-realm \
-s name="group-mapper" \
-s providerId=group-ldap-mapper \
-s providerType=org.keycloak.storage.ldap.mappers.LDAPStorageMapper \
-s parentId=$LDAP_ID \
-s 'config.groups.dn=["ou=Groups,dc=example,dc=com"]' \
-s 'config.group.name.ldap.attribute=["cn"]' \
-s 'config.group.object.classes=["groupOfNames"]' \
-s 'config.membership.ldap.attribute=["member"]' \
-s 'config.membership.attribute.type=["DN"]' \
-s 'config.membership.user.ldap.attribute=["uid"]' \
-s 'config.mode=["READ_ONLY"]' \
-s 'config.drop.non.existing.groups.during.sync=["false"]'
# Trigger full sync
kcadm.sh create user-storage/$LDAP_ID/sync -r my-realm \
-s action=triggerFullSync
12. Troubleshooting LDAP Issues
12.1 Connection Issues
| Error | Cause | Solution |
|---|---|---|
javax.naming.CommunicationException | Unable to connect to LDAP server | Check network, firewall, port 389/636 |
javax.naming.AuthenticationException | Incorrect Bind DN or Bind Credential | Verify bind credentials with ldapsearch |
SSLHandshakeException | Certificate not trusted | Import CA cert into truststore |
Connection timeout | LDAP server does not respond | Increase connection timeout, check DNS |
# Test LDAP connection
ldapsearch -x -H ldap://ldap.example.com:389 \
-D "cn=admin,dc=example,dc=com" -W \
-b "ou=People,dc=example,dc=com" \
"(objectClass=inetOrgPerson)" uid mail cn
# Test LDAPS connection
ldapsearch -x -H ldaps://ldap.example.com:636 \
-D "cn=admin,dc=example,dc=com" -W \
-b "dc=example,dc=com" "(uid=testuser)"
# Bật debug logging trong Keycloak
bin/kc.sh start-dev \
--log-level=org.keycloak.storage.ldap:DEBUG
12.2 Sync Failures
| Error | Cause | Solution |
|---|---|---|
User ... already exists | Username conflict between LDAP and local users | Delete local user or change username mapping |
Size limit exceeded | LDAP server limits the number of returned results | Configure paging on LDAP server, or add LDAP filter to reduce scope |
Referral | LDAP returns referral instead of result | Set Referral = follow in connection settings |
12.3 Mapper Problems
# Kiểm tra LDAP attributes có tồn tại
ldapsearch -x -H ldap://ldap.example.com:389 \
-D "cn=admin,dc=example,dc=com" -W \
-b "uid=testuser,ou=People,dc=example,dc=com" \
"*" "+"
# Kiểm tra group membership
ldapsearch -x -H ldap://ldap.example.com:389 \
-D "cn=admin,dc=example,dc=com" -W \
-b "ou=Groups,dc=example,dc=com" \
"(member=uid=testuser,ou=People,dc=example,dc=com)" cn
13. Best Practices
- Always use LDAPS or StartTLS — avoid sending credentials in plaintext
- Use separate service account for Bind DN — do not use admin account
- Search Scope Limit — use Custom User LDAP Filter to import only necessary users
- Enable Connection Pool — reduces connection creation overhead
- Configure the sync period appropriately — too short causes heavy load on LDAP, too long causes stale data
- Monitor sync logs — Keycloak records detailed sync process logs
- Test with READ_ONLY first — when first configuring, use READ_ONLY to verify before transferring WRITABLE
- Backup Keycloak DB before big sync — full sync can import thousands of users