[[_ldap]]

=== Lightweight Directory Access Protocol (LDAP) and Active Directory

{project_name} includes an LDAP/AD provider. You can federate multiple different LDAP servers in one {project_name} realm and map LDAP user attributes into the {project_name} common user model.

By default, {project_name} maps the username, email, first name, and last name of the user account, but you can also configure additional <<_ldap_mappers,mappings>>. {project_name}'s LDAP/AD provider supports password validation using LDAP/AD protocols and storage, edit, and synchronization modes.

==== Configuring federated LDAP storage

.Procedure
. Click *User Federation* in the menu.
+
.User federation
image:images/user-federation.png[User federation]
+
. Click *Add LDAP providers*.
+
{project_name} brings you to the LDAP configuration page.
+
.Add LDAP provider
image:images/user-fed-ldap.png[User federation]

==== Storage mode

{project_name} imports users from LDAP into the local {project_name} user database. This copy of the user database synchronizes on-demand or through a periodic background task. An exception exists for synchronizing passwords. {project_name} never imports passwords. Password validation always occurs on the LDAP server.

The advantage of synchronization is that all {project_name} features work efficiently because any required extra per-user data is stored locally. The disadvantage is that each time {project_name} queries a specific user for the first time, {project_name} performs a corresponding database insert. Also, when imported users are returned as part of a search operation, a corresponding LDAP search is performed for each one to check if the user still exists in LDAP and do some basic validation.

You can synchronize the import with your LDAP server. Import synchronization is unnecessary when LDAP mappers always read particular attributes from the LDAP rather than the database.

You can use LDAP with {project_name} without importing users into the {project_name} user database. The LDAP server backs up the common user model that the {project_name} runtime uses. If LDAP does not support data that a {project_name} feature requires, that feature will not work. The advantage of this approach is that you do not have the resource usage of importing and synchronizing copies of LDAP users into the {project_name} user database.

The *Import Users* switch on the LDAP configuration page controls this storage mode. To import users, toggle this switch to *ON*.

[NOTE]
====
If you disable *Import Users*, you cannot save user profile attributes into the {project_name} database. Also, you cannot save metadata except for user profile metadata mapped to the LDAP. This metadata can include role mappings, group mappings, and other metadata based on the LDAP mappers' configuration.

When you attempt to change the non-LDAP mapped user data, the user update is not possible. For example, you cannot disable the LDAP mapped user unless the user's `enabled` flag maps to an LDAP attribute.
====

[NOTE]
====
When working with imported users, {project_name} performs a LDAP search when the user is queried to validate the user and decorate it so that the configured mappers work properly. This means that extra care must be taken when performing unfiltered user searches that may fetch a big number of users as a LDAP search will be issued for every imported user that is found, possibly affecting the performance in a negative way.

Operations that fetch a single user (for example during login) are usually cached and should not be impacted by this extra LDAP search that is performed when the user is fetched for the first time.
====

==== Edit mode

Users and admins can modify user metadata, users through the <<_account-service, Account Console>>, and administrators through the Admin Console. The `Edit Mode` configuration on the LDAP configuration page defines the user's LDAP update privileges.

READONLY::
You cannot change the username, email, first name, last name, and other mapped attributes. {project_name} shows an error anytime a user attempts to update these fields. Password updates are not supported.

WRITABLE::
You can change the username, email, first name, last name, and other mapped attributes and passwords and synchronize them automatically with the LDAP store.

UNSYNCED::
{project_name} stores changes to the username, email, first name, last name, and passwords in {project_name} local storage, so the administrator must synchronize this data back to LDAP. In this mode, {project_name} deployments can update user metadata on read-only LDAP servers. This option also applies when importing users from LDAP into the local {project_name} user database.

[NOTE]
====
When {project_name} creates the LDAP provider, {project_name} also creates a set of initial <<_ldap_mappers,LDAP mappers>>. {project_name} configures these mappers based on a combination of the *Vendor*, *Edit Mode*, and *Import Users* switches. For example, when edit mode is UNSYNCED, {project_name} configures the mappers to read a particular user attribute from the database and not from the LDAP server. However, if you later change the edit mode, the mapper's configuration does not change because it is impossible to detect if the configuration changes changed in UNSYNCED mode. Decide the *Edit Mode* when creating the LDAP provider. This note applies to *Import Users* switch also.
====

==== Other configuration options

Console Display Name::
The name of the provider to display in the admin console.

Priority::
The priority of the provider when looking up users or adding a user.

Sync Registrations::
Toggle this switch to *ON* if you want new users created by {project_name} added to LDAP.

Allow Kerberos authentication::
Enable Kerberos/SPNEGO authentication in the realm with user data provisioned from LDAP. For more information, see the <<_kerberos,Kerberos section>>.

Remove invalid users during searches::
Remove users from the local database if they are not available from the user storage when executing searches. If this is true, users no longer available from their corresponding user storage will be deleted from the local database whenever trying to look up users. If false, then users previously imported from the user storage will be kept in the local database, as read-only and disabled, even if that user is no longer available from the user storage. For example, user was deleted directly from LDAP or the `Users DN` is invalid. Note that this behavior will only happen when the user is not yet cached.

Relative User Creation DN::
Relative DN from the `Users DN` where new users will be created. This allows users to be created in a sub-DN of the parent `Users DN` when using a `subtree` search scope. For example, if the `Users DN` is set to `ou=people,dc=myorg,dc=com` and the `Relative User Creation DN` is set to `ou=engineering`, users will be fetched from the `Users DN` and all sub-DNs, but new users will be stored in `ou=engineering,ou=people,dc=myorg,dc=com`. In other words, {project_name} concatenates the `Relative User Creation DN` with the `Users DN` (a comma is added automatically when concatenating the DNs) and uses this resulting DN to store users

A similar property is also available in the group and role mappers, allowing groups and roles to be added to a sub-DN of the base DN that is used to search for the groups/roles.

Other options::
Hover the mouse pointer over the tooltips in the Admin Console to see more details about these options.

==== Connecting to LDAP over SSL

When you configure a secure connection URL to your LDAP store (for example,`ldaps://myhost.com:636`), {project_name} uses SSL to communicate with the LDAP server. Configure a truststore on the {project_name} server side so that {project_name} can trust the SSL connection to LDAP - see https://www.keycloak.org/server/keycloak-truststore[Configuring a Truststore] {section}.

The `Use Truststore SPI` configuration property is deprecated.  It should normally be left as `Always`.

==== Synchronizing LDAP users to {project_name}

If you set the *Import Users* option, the LDAP Provider handles importing LDAP users into the {project_name} local database. The first time a user logs in or is returned as part of a user query (e.g. using the search field in the admin console), the LDAP provider imports the LDAP user into the {project_name} database. During authentication, the LDAP password is validated.

By default, {project_name} does not support the username and email attributes with case-sensitive values when storing users to the local database. The value for these attributes will be stored in lower-case in the local database.
However, if the *Import Users* option is disabled, {project_name} will not lower-case the username and email attributes when querying users from LDAP.
This behavior allows you to use case-sensitive usernames and emails when *Import Users* is disabled. Note that this behavior applies only to username and email attributes. Other attributes remain case-sensitive.

It is recommended to not use case-sensitive usernames and emails when using LDAP with {project_name}, as some features in {project_name} may not work correctly with case-sensitive usernames and emails.

If you want to sync all LDAP users into the {project_name} database, configure and enable the *Sync Settings* on the LDAP provider configuration page.

Two types of synchronization exist:

Periodic Full sync::
This type synchronizes all LDAP users into the {project_name} database. The LDAP users already in {project_name}, but different in LDAP, directly update in the {project_name} database.

Periodic Changed users sync::
When synchronizing, {project_name} creates or updates users created or updated after the last sync only.

The best way to synchronize is to click *Synchronize all users* when you first create the LDAP provider, then set up periodic synchronization of changed users.

[[_ldap_mappers]]
==== LDAP mappers

LDAP mappers are `listeners` triggered by the LDAP Provider. They provide another extension point to LDAP integration. LDAP mappers are triggered when:

* Users log in by using LDAP.
* Users initially register.
* The Admin Console queries a user.

When you create an LDAP Federation provider, {project_name} automatically provides a set of `mappers` for this provider. This set is changeable by users, who can also develop mappers or update/delete existing ones.

User Attribute Mapper::
This mapper specifies which LDAP attribute maps to the attribute of the {project_name} user. For example, you can configure the `mail` LDAP attribute to the `email` attribute in the {project_name} database. For this mapper implementation, a one-to-one mapping always exists.

FullName Mapper::
This mapper specifies the full name of the user. {project_name} saves the name in an LDAP attribute (usually `cn`) and maps the name to the `firstName` and `lastname` attributes in the {project_name} database. Having `cn` to contain the full name of the user is common for LDAP deployments.

[NOTE]
====
When you register new users in {project_name} and `Sync Registrations` is ON for the LDAP provider, the fullName mapper permits falling back to the username. This fallback is  useful when using Microsoft Active Directory (MSAD). The common setup for MSAD is to configure the `cn` LDAP attribute as fullName and, at the same time, use the `cn` LDAP attribute as the `RDN LDAP Attribute` in the LDAP provider configuration. With this setup, {project_name} falls back to the username. For example, if you create {project_name} user "john123" and leave firstName and lastName empty, then the fullname mapper saves "john123" as the value of the `cn` in LDAP. When you enter "John Doe" for firstName and lastName later, the fullname mapper updates LDAP `cn` to the "John Doe" value as falling back to the username is unnecessary.
====

Hardcoded Attribute Mapper::
This mapper adds a hardcoded attribute value to each {project_name} user linked with LDAP. This mapper can also force values for the `enabled` or `emailVerified` user properties.

Role Mapper::
This mapper configures role mappings from LDAP into {project_name} role mappings. A single role mapper can map LDAP roles (usually groups from a particular branch of the LDAP tree) into roles corresponding to a specified client's realm roles or client roles. You can configure more Role mappers for the same LDAP provider. For example, you can specify that role mappings from groups under `ou=main,dc=example,dc=org` map to realm role mappings, and role mappings from groups under `ou=finance,dc=example,dc=org` map to client role mappings of client `finance`.

Hardcoded Role Mapper::
This mapper grants a specified {project_name} role to each {project_name} user from the LDAP provider.

Group Mapper::
This mapper maps LDAP groups from a branch of an LDAP tree into groups within {project_name}. This mapper also propagates user-group mappings from LDAP into user-group mappings in {project_name}.

MSAD User Account Mapper::
This mapper is specific to Microsoft Active Directory (MSAD). It can integrate the MSAD user account state into the {project_name} account state, such as enabled account or  expired password. This mapper uses the `userAccountControl`, and `pwdLastSet` LDAP attributes, specific to MSAD and are not the LDAP standard. For example, if the value of `pwdLastSet` is `0`, the {project_name} user must update their password. The result is an UPDATE_PASSWORD required action added to the user. If the value of  `userAccountControl` is `514` (disabled account), the {project_name} user is disabled.

Certificate Mapper::
This mapper maps X.509 certificates. {project_name} uses it in conjunction with X.509 authentication and `Full certificate in PEM format` as an identity source. This mapper behaves similarly to the `User Attribute Mapper`, but {project_name} can filter for an LDAP attribute storing a PEM or DER format certificate. Enable `Always Read Value From LDAP` with this mapper.

User Attribute mappers that map basic {project_name} user attributes, such as username, firstname, lastname, and email, to corresponding LDAP attributes. You can extend these and provide your own additional attribute mappings. The Admin Console provides tooltips to help with configuring the corresponding mappers.

[[_ldap_password_hashing]]
==== Password hashing

When {project_name} updates a password, {project_name} sends the password in plain-text format. This action is different from updating the password in the built-in {project_name} database, where {project_name} hashes and salts the password before sending it to the database. For LDAP, {project_name} relies on the LDAP server to hash and salt the password.

By default, LDAP servers such as MSAD, RHDS, or FreeIPA hash and salt passwords. Other LDAP servers such as OpenLDAP store the passwords in plain-text unless you use the _LDAPv3 Password Modify Extended Operation_ as described in https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.3[RFC3062]. Enable the LDAPv3 Password Modify Extended Operation in the LDAP configuration page. See the documentation of your LDAP server for more details. https://directory.apache.org/apacheds/advanced-ug/4.1.1.4-ss-password-hash.html[Configure ApacheDS to hash and salt passwords automatically] by enabling the passwordHashing interceptor.

WARNING: Always verify that user passwords are properly hashed and not stored as plaintext by inspecting a changed
directory entry using `ldapsearch` and base64 decode the `userPassword` attribute value.

[[_ldap_connection_pool]]
==== Configuring the connection pool

For more efficiency when managing LDAP connections and to improve performance when handling multiple connections, you can
enable connection pooling. By doing that, when a connection is closed, it will be returned to the pool for future use therefore
reducing the cost of creating new connections all the time.

The LDAP connection pool configuration is configured using the following system properties:

[cols="2*", options="header"]
|===
|Name
|Description
| `com.sun.jndi.ldap.connect.pool.authentication` | A list of space-separated authentication types of connections that may be pooled. Valid types are "none", "simple", and "DIGEST-MD5"
| `com.sun.jndi.ldap.connect.pool.initsize` | The string representation of an integer that represents the number of connections per connection identity to create when initially creating a connection for the identity
| `com.sun.jndi.ldap.connect.pool.maxsize` | The string representation of an integer that represents the maximum number of connections per connection identity that can be maintained concurrently. Note setting this value too low may cause contention in obtaining LDAP connections. See also `com.sun.jndi.ldap.connect.timeout`.
| `com.sun.jndi.ldap.connect.pool.prefsize` | The string representation of an integer that represents the preferred number of connections per connection identity that should be maintained concurrently
| `com.sun.jndi.ldap.connect.pool.timeout` | The string representation of an integer that represents the number of milliseconds that an idle connection may remain in the pool without being closed and removed from the pool
| `com.sun.jndi.ldap.connect.pool.protocol` | A list of space-separated protocol types of connections that may be pooled. Valid types are "plain" and "ssl"
| `com.sun.jndi.ldap.connect.pool.debug` | A string that indicates the level of debug output to produce. Valid values are "fine" (trace connection creation and removal) and "all" (all debugging information)
| `com.sun.jndi.ldap.connect.timeout` | The string representation of an integer that represents how long in milliseconds obtaining a connection should take. This is also applicable to wait times due to connection pool contention. Effectively defaults to 5000.
|===

By default, connection pooling is enabled for both `plain` and `ssl` protocols.

For more details, see the link:https://docs.oracle.com/javase/jndi/tutorial/ldap/connect/config.html[Java LDAP Connection Pooling Configuration] documentation.

To set any of these properties, you can set the `JAVA_OPTS_APPEND` environment variable:

[source,bash]
----
export JAVA_OPTS_APPEND=-Dcom.sun.jndi.ldap.connect.pool.initsize=10 -Dcom.sun.jndi.ldap.connect.pool.maxsize=50
----

[[_ldap_troubleshooting]]
==== Troubleshooting

It is useful to increase the logging level to TRACE for the category `org.keycloak.storage.ldap`. With this setting, many logging messages are sent
to the server log in the `TRACE` level, including the logging for all queries to the LDAP server and the parameters, which were
used to send the queries. When you are creating any LDAP question on user forum or JIRA, consider attaching the server log with
enabled TRACE logging. If it is too big, the good alternative is to include just the snippet from server log with the messages, which were
added to the log during the operation, which causes the issues to you.


- When you create an LDAP provider, a message appears in the server log in the INFO level starting with:


```
Creating new LDAP Store for the LDAP storage provider: ...
```
It shows the configuration of your LDAP provider. Before you are asking the questions or reporting bugs, it will be nice to include this
message to show your LDAP configuration. Eventually feel free to replace some config changes, which you do not want to include, with some
placeholder values. One example is `bindDn=some-placeholder` . For `connectionUrl`, feel free to replace it as well, but it is generally
useful to include at least the protocol, which was used (`ldap` vs `ldaps`)`. Similarly it can be useful to include the details for
configuration of your LDAP mappers, which are displayed with the message like this at the DEBUG level:
```
Mapper for provider: XXX, Mapper name: YYY, Provider: ZZZ ...
```
Note those messages are displayed just with the enabled DEBUG logging.

- For tracking the performance or connection pooling issues, consider setting the value of property `com.sun.jndi.ldap.connect.pool.debug` to `all`. This change adds many additional messages to the server log with the included logging for the LDAP connection
pooling. As a result, you can track the issues related to connection pooling or performance. For more details, see <<_ldap_connection_pool,Configuring the connection pool>>.

NOTE: After changing the configuration of connection pooling, you may need to restart the {project_name} server to enforce re-initialization
of the LDAP provider connection.

If no more messages appear for connection pooling even after server restart, it can indicate that connection pooling does not work
with your LDAP server.

- For the case of reporting LDAP issue, you may consider to attach some part of your LDAP tree with the target data, which causes issues
in your environment. For example if login of some user takes lot of time, you can consider attach his LDAP entry showing count of `member` attributes
of various "group" entries. In this case, it might be useful to add if those group entries are mapped to some Group LDAP mapper (or Role LDAP Mapper)
in {project_name} and so on.



