> For the complete documentation index, see [llms.txt](https://docs.opendatadiscovery.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.opendatadiscovery.org/configuration-and-deployment/enable-security/authentication/ldap.md).

# LDAP

ODD Platform can be configured to use existing LDAP server for users authentication. There are several properties, that need to be set in order to enable this kind of security.

**Define authentication type**

{% tabs %}
{% tab title="YAML" %}

```yaml
auth:
    type: LDAP
```

{% endtab %}

{% tab title="Environment variables" %}

```
AUTH_TYPE=LDAP
```

{% endtab %}
{% endtabs %}

#### Connect to LDAP server

There are 3 properties, which are responsible for connecting to LDAP server

* `auth.ldap.url`: LDAP server url (required)
* `auth.ldap.username`: The username (principal) to use when authenticating with the LDAP server
* `auth.ldap.password`: The password (credentials) to use when authenticating with the LDAP server

{% hint style="info" %}
Username and password are not required. If they are not set, operations will be performed by using an anonymous (unauthenticated) context
{% endhint %}

{% tabs %}
{% tab title="YAML" %}

```yaml
auth:
    ldap:
        url: "ldap://localhost:389"
        username: admin
        password: password
```

{% endtab %}

{% tab title="Environment variables" %}

```
AUTH_LDAP_URL=ldap://localhost:389
AUTH_LDAP_USERNAME=admin
AUTH_LDAP_PASSWORD=password
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
**Use `ldaps://` for any server reachable beyond localhost.** The platform passes `auth.ldap.url` to the LDAP client exactly as written and does not enforce a scheme. With an `ldap://` URL — including the `ldap://localhost:389` example above — the bind credentials and every end user's login password travel to the directory in cleartext. No warning is logged at startup. Set the URL to `ldaps://your-server:636` unless the directory is on the same host as the platform.
{% endhint %}

{% hint style="warning" %}
**`auth.ldap.password` is held in plaintext.** The bind password is read straight from configuration and kept as a plain string in the running process; it is not encrypted and is not redacted from the platform's own configuration view. Treat it as a secret at rest: supply it through your deployment's secret mechanism rather than committing it to a values file, and restrict who can read the platform's environment and configuration.
{% endhint %}

#### Perform users search

There are 2 ways of how to retrieve users in LDAP server.

1. Define DN pattern of user names. This is great, when all users are stored under a single node in a directory.
2. Setup LDAP search filter.

{% tabs %}
{% tab title="YAML" %}
**DN pattern**

This is an example of how user DN pattern can be defined. In this case DN for the user will be built by substituting login in the supplied pattern instead of 0.

```yaml
auth:
    ldap:
        dn-pattern: "uid={0},ou=people,dc=mycompany,dc=com"
```

**Search filter**

This is an example of using search filter instead of DN pattern. If a user search base isn’t supplied, the search will be performed from the root.

```yaml
auth:
    ldap:
        user-filter:
            search-base: "ou=people,dc=mycompany,dc=com"
            filter: "(uid={0})"
```

{% endtab %}

{% tab title="Environment variables" %}
**DN pattern**

This is an example of how user DN pattern can be defined. In this case DN for the user will be built by substituting login in the supplied pattern

```
AUTH_LDAP_DN_PATTERN="uid={0},ou=people"
```

**Search filter**

This is an example of using search filter instead of DN pattern. If a user search base isn’t supplied, the search will be performed from the root.

```
AUTH_LDAP_USER_FILTER_SEARCH_BASE="ou=people"
AUTH_LDAP_USER_FILTER_FILTER="uid={0}"
```

{% endtab %}
{% endtabs %}

{% hint style="warning" %}
It is required to set up one of those search methods, otherwise application start will fail
{% endhint %}

#### Define admin groups

ODD platform can get LDAP groups, which the user belongs to. Thus it is possible to define which groups will grant admin privileges. There are several properties that need to be set in order to allow ODD platform to do this:

* `auth.ldap.groups.search-base`: The base DN from which the search for group membership should be performed. By default it will be performed from the root.
* `auth.ldap.groups.filter`: The pattern used to find the groups a user belongs to. ODD platform does not set this default itself — when you leave it unset, the platform never overrides the group search filter, and Spring Security's `DefaultLdapAuthoritiesPopulator` applies its own built-in default of

  `(member={0})`, where the user's DN is substituted for `{0}`.
* `auth.ldap.groups.admin-groups`: List of groups, which members will be granted admin permissions.

{% tabs %}
{% tab title="YAML" %}

```yaml
auth:
    ldap:
        groups:
            search-base: "dc=mycompany,dc=com"
            filter: "(member={0})"
            admin-groups: admin
```

{% endtab %}

{% tab title="Environment variables" %}

```
AUTH_LDAP_GROUPS_SEARCH_BASE="dc=mycompany,dc=com"
AUTH_LDAP_GROUPS_FILTER="(member={0})"
AUTH_LDAP_GROUPS_ADMIN_GROUPS=admin
```

{% endtab %}
{% endtabs %}

#### Active directory

If you are using Active Directory as LDAP server there are additional properties, that need to be set

* `auth.ldap.active-directory.enabled` : Must be set to `true`
* `auth.ldap.active-directory.domain`: Domain name

{% hint style="warning" %}
**Set `domain` whenever you set `enabled: true`.** The platform does not check that the two go together: a configuration with `active-directory.enabled: true` and no `domain` starts up without an error and then attempts Active Directory binds with no domain, which fail at sign-in time rather than at startup. Always provide both values together.
{% endhint %}

{% tabs %}
{% tab title="YAML" %}

```yaml
auth:
    ldap:
        active-directory:
            enabled: true
            domain: "example.com"
```

{% endtab %}

{% tab title="Environment variables" %}

```
AUTH_LDAP_ACTIVE_DIRECTORY_ENABLED=true
AUTH_LDAP_ACTIVE_DIRECTORY_DOMAIN="example.com"
```

{% endtab %}
{% endtabs %}

**Final configuration example**

{% tabs %}
{% tab title="YAML" %}

```yaml
auth:
    type: LDAP
    ldap:
        url: "ldap://localhost:389"
        username: admin
        password: password
        dn-pattern: "uid={0},ou=people,dc=mycompany,dc=com"
        groups:
            search-base: "dc=mycompany,dc=com"
            filter: "(member={0})"
            admin-groups: admin
```

{% endtab %}

{% tab title="Environment variables" %}

```
AUTH_TYPE=LDAP
AUTH_LDAP_URL=ldap://localhost:389
AUTH_LDAP_USERNAME=admin
AUTH_LDAP_PASSWORD=password
AUTH_LDAP_DN_PATTERN="uid={0},ou=people"
AUTH_LDAP_GROUPS_SEARCH_BASE="dc=mycompany,dc=com"
AUTH_LDAP_GROUPS_FILTER="(member={0})"
AUTH_LDAP_GROUPS_ADMIN_GROUPS=admin
```

{% endtab %}
{% endtabs %}

#### Admin promotion (group-name matching)

For the cross-mode comparison of how every auth mode and OAuth provider grants ADMIN, see [Admin promotion across providers](/configuration-and-deployment/enable-security/admin-promotion.md). The notes below cover LDAP-specific behaviour only.

`auth.ldap.groups.admin-groups` accepts a list of group names whose members the platform promotes to `ADMIN`. A user is promoted when one of their LDAP group names equals a configured value, compared **case-insensitively on the whole name**. The comparison is not a substring or prefix match: each configured token must match an entire group name, ignoring only letter case.

| `admin-groups` value | LDAP group the user belongs to | Promoted to `ADMIN`?  |
| -------------------- | ------------------------------ | --------------------- |
| `Admin`              | `Admin`                        | Yes — same name       |
| `Admin`              | `admin`                        | Yes — case is ignored |
| `Admin`              | `Administrator`                | No — different name   |
| `ops`                | `devops`                       | No — different name   |

Because matching is whole-name, list every admin group explicitly. Adding `ops` does not promote members of `devops` or `dataops`; if those groups should also be admins, name each of them in `admin-groups`.

**When `auth.ldap.groups.admin-groups` is empty or unset**

If `auth.ldap.groups` is configured but `admin-groups` is empty (`admin-groups: []` or simply omitted), every authenticated LDAP user is granted the `USER` role only — there is no path to `ADMIN` via LDAP under this configuration, and no boot warning surfaces the absence. A deployment that uncomments the LDAP block but forgets the `admin-groups` entry has no possible ADMIN user via LDAP. Decide on the `admin-groups` value at the same time you author the rest of the LDAP block, even if your initial value is a single well-known operator group.

A platform-side fix that fail-fast-warns when `auth.ldap.groups` is configured without `admin-groups` is tracked upstream.

#### Cross-mode user-name collision (activity feed read paths)

The platform's [activity-feed](/features/active-platform-features/activity-feed.md) read paths join the `USER_OWNER_MAPPING` table on `OIDC_USERNAME` only — the join does **not** discriminate by auth provider. If your deployment has historically used multiple auth modes (for example, started on `LOGIN_FORM` with seed users, then enabled `LDAP`), a user named `alice` who signed in via `LOGIN_FORM` and a user named `alice` who signed in via `LDAP` can resolve to the same `OwnerPojo` when activity-feed rows are rendered. The most recent `USER_OWNER_MAPPING` row for the literal username wins each lookup; results are not deterministic across replicas.

**Operator mitigation today:** never reuse usernames across auth modes when migrating. If you migrate from `LOGIN_FORM` to `LDAP`, delete the `USER_OWNER_MAPPING` rows for the `LOGIN_FORM` usernames before introducing same-named LDAP users.

**Forensic note:** do not rely on activity-feed Owner attribution for incident response in multi-mode deployments without verifying the auth mode of the actor through the underlying `activity.created_by` value.

A platform-side fix that discriminates the join by `PROVIDER` is tracked upstream. The same caveat applies to deployments running `LOGIN_FORM` on top of an earlier `LDAP` history — see the matching note on the [Login form](/configuration-and-deployment/enable-security/authentication/login-form.md#cross-mode-user-name-collision-activity-feed-read-paths) page.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.opendatadiscovery.org/configuration-and-deployment/enable-security/authentication/ldap.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
