> ## Documentation Index
> Fetch the complete documentation index at: https://docs.strata.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Modernize LDAP Authentication

By the end of this guide, you will have a Maverics Orchestrator configured as a virtual LDAP directory -- authenticating LDAP bind requests against a modern cloud identity provider and returning directory data to LDAP-dependent applications, without changing the application's LDAP configuration.

## What Is LDAP Provider Mode?

Many enterprise applications -- especially legacy and commercial off-the-shelf (COTS) products -- depend on LDAP for authentication and directory lookups. These applications perform LDAP bind operations to validate user credentials and LDAP search operations to retrieve user attributes like group memberships, email addresses, and display names.

The Maverics Orchestrator's LDAP Provider mode creates a virtual LDAP directory that presents a standard LDAP interface to these applications while sourcing identity data from modern cloud identity providers such as Microsoft Entra ID, Okta, or PingFederate. Applications continue using their existing LDAP configuration -- the Orchestrator handles the translation between LDAP protocol operations and cloud IdP APIs.

The most common deployment pattern pairs LDAP Provider with [HTTP Proxy](/guides/authentication/http-proxy-auth) mode in a **facade** architecture, where the Orchestrator handles both the browser-facing authentication (via HTTP Proxy) and the application's backend LDAP operations (via LDAP Provider). See Step 6 for details on this pattern.

## Prerequisites

* **A running Maverics Orchestrator** -- If you have not installed the Orchestrator yet, follow the [Quick Start guide](/guides/getting-started/quick-start) or see the [installation reference](/reference/orchestrator/installation).
* **An identity provider account** -- You need credentials for an identity provider such as [Microsoft Entra ID](/reference/orchestrator/identity-fabric/azure-ad), [Okta](/reference/orchestrator/identity-fabric/okta), or [PingFederate](/reference/orchestrator/identity-fabric/pingone). This IdP will serve as the upstream source of identity data that the Orchestrator translates into LDAP responses.
* **A target LDAP-dependent application** -- An application that authenticates users via LDAP bind operations or retrieves user attributes via LDAP search queries.
* **Service Extension development capability (Go)** -- Unlike other Orchestrator modes, the LDAP Provider **requires** Go-based Service Extensions for its core operations. There is no declarative-only configuration path. You will need to write Service Extensions that handle LDAP bind authentication and search queries. See [Service Extensions reference](/reference/orchestrator/service-extensions).
* **Knowledge of your application's LDAP expectations** -- You must know the bind DN format your application uses, the search base DN, the LDAP attributes it expects in search results (e.g., `cn`, `sAMAccountName`, `memberOf`), and the bind method (simple bind or SASL/NTLM).

<Warning>
  **Service Extensions are required.** Every LDAP Provider integration requires Go-based Service Extensions to handle LDAP operations (bind authentication, search queries, credential lookups). The LDAP Provider cannot function without them. Plan for Service Extension development as part of your integration timeline.
</Warning>

## Configure LDAP Provider Mode

<Steps>
  <Step title="Create an Orchestrator Deployment">
    In the Maverics Console, all configuration lives within a **Deployment** -- a managed Orchestrator instance with its own configuration, storage provider, and signing keys. Before you can configure the LDAP Provider, you need a Deployment to hold the configuration.

    <Tabs>
      <Tab title="Console UI">
        1. Log in to the [Maverics Console](https://maverics.strata.io).
        2. Navigate to **Deployments** in the sidebar and click **Create**.
        3. Enter a name for your deployment (e.g., `LDAP Provider`).
        4. Select a deployment provider. The Console supports several options:

           * **Evaluation** -- Strata-managed storage, no infrastructure needed. Best for getting started.
           * **AWS S3**, **Azure Blob Storage**, or **Google Cloud Storage** -- customer-managed cloud storage for production.
           * **GitHub** or **GitLab** -- deploy bundles to a Git repository.
           * **Download Only** -- manual bundle download for air-gapped environments.

           See [Publishing Deployment Configs overview](/reference/console/config-publishing#deployment-providers) for provider setup details.
        5. Click **Create**.

        Your new deployment opens automatically. You can now configure the LDAP Provider, identity connectors, and Service Extensions within it.
      </Tab>

      <Tab title="Configuration">
        When using direct configuration, no deployment setup is needed -- configuration is defined directly in the `maverics.yaml` file and delivered to the Orchestrator via the `-config` flag or `MAVERICS_CONFIG` environment variable. See [Installation](/reference/orchestrator/installation) for details on configuration delivery options.
      </Tab>
    </Tabs>
  </Step>

  <Step title="Enable the LDAP Provider">
    The LDAP Provider must be enabled and configured at the deployment level. This sets up the virtual LDAP directory server that LDAP-dependent applications will connect to.

    <Tabs>
      <Tab title="Console UI">
        1. Open your deployment and click the **gear icon** in the sidebar to open **Deployment Settings**.
        2. Find the **LDAP Provider** section.
        3. Toggle **Enable** to on.
        4. Select the **Protocol**: `ldap://` (unencrypted) or `ldaps://` (TLS-encrypted). Use `ldaps://` for production deployments.
        5. Set the **URI** -- the address and port the LDAP Provider listens on (e.g., `ldaps://0.0.0.0:636` for TLS on the standard LDAPS port, or `ldap://0.0.0.0:389` for unencrypted).
        6. If using `ldaps://`, provide the **TLS Certificate** file path and **TLS Key File** path.
        7. Click **Save**.

        <Note>
          The LDAP Provider Deployment Settings fields are documented in detail in the [LDAP Provider reference](/reference/modes/ldap-provider#setup). Use the Console UI tab there for field-by-field descriptions.
        </Note>
      </Tab>

      <Tab title="Configuration">
        Enable the LDAP Provider in your `maverics.yaml` file. The `ldapProvider` top-level key configures the virtual directory server.

        ```yaml maverics.yaml theme={null}
        tls:
          ldap-server-tls:
            certFile: /etc/maverics/certs/ldap-server.pem
            keyFile: /etc/maverics/certs/ldap-server-key.pem

        ldapProvider:
          enabled: true
          uri: ldaps://0.0.0.0:636
          tls: ldap-server-tls
          readDeadline: 5m
          writeDeadline: 5m
          rootDSE:
            attributes:
              namingContexts:
                - dc=example,dc=com
        ```

        The `uri` sets the listen address. Use `ldaps://` with a named TLS profile for production. The `rootDSE.attributes.namingContexts` should match the base DN that your LDAP-dependent application expects when it discovers the directory's capabilities.

        See [LDAP Provider Reference](/reference/modes/ldap-provider) for all available fields, including SASL/NTLM authentication and advanced Root DSE configuration.
      </Tab>
    </Tabs>
  </Step>

  <Step title="Connect your identity provider">
    An identity provider (IdP) is the service that manages your user accounts -- Microsoft Entra ID, Okta, Google Workspace, or an on-premises directory. Connecting the Orchestrator to your IdP tells it where to source identity data for LDAP responses.

    The Orchestrator uses [Identity Fabric](/reference/orchestrator/identity-fabric) connectors to communicate with your IdP. The connector provides the upstream identity data that your Service Extensions (configured in the next steps) will translate into LDAP responses.

    You will need your IdP's client ID, client secret, and discovery URL. These credentials allow the Orchestrator to query the IdP for user authentication and attribute data.

    <Tabs>
      <Tab title="Console UI">
        1. Navigate to **Identity Fabric** in the sidebar and click **Create**.
        2. Select your identity provider from the list. Choose a provider-specific option (such as **Okta (OIDC)**, **Microsoft Entra ID (OIDC)**, or **Auth0 (OIDC)**) or select **Generic OIDC Configuration** for any OIDC-compliant provider. SAML and LDAP connector options are also available.
        3. Fill in the connector form:

        | Field                        | Required | Description                                                                                                                       |
        | ---------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------- |
        | Name                         | Yes      | A friendly name for your identity provider (e.g., `upstream-idp`).                                                                |
        | OIDC Well Known URL          | Yes      | Your IdP's OIDC discovery endpoint (e.g., `https://your-idp.example.com/.well-known/openid-configuration`).                       |
        | OAuth Client ID              | Yes      | The client ID from the application you registered in your IdP for Maverics.                                                       |
        | Client Authentication Method | Yes      | Select **OAuth Client Secret** (default) or **OAuth JWT Client Authentication**.                                                  |
        | OAuth Client Secret          | Yes      | The client secret from your IdP. For production, use a [secret provider](/reference/orchestrator/configuration/secret-providers). |
        | Redirect URLs                | Yes      | The callback URL where your IdP redirects after authentication.                                                                   |
        | Scopes                       | No       | Space-separated OIDC scopes (e.g., `openid profile email`).                                                                       |

        4. Click **Save**.
      </Tab>

      <Tab title="Configuration">
        Define a connector in the `connectors` section of your `maverics.yaml` file. The connector specifies how the Orchestrator communicates with your identity provider to retrieve user data.

        ```yaml maverics.yaml theme={null}
        connectors:
          - name: upstream-idp
            type: oidc
            oidcWellKnownURL: https://your-idp.example.com/.well-known/openid-configuration
            oauthClientID: ldap-bridge
            oauthClientSecret: <idp.clientSecret>
        ```

        Replace `your-idp.example.com` with your identity provider's domain. The `oauthClientSecret` uses the `<idp.clientSecret>` secret reference syntax -- the Orchestrator retrieves this value at runtime from your [secret provider](/reference/orchestrator/configuration/secret-providers).
      </Tab>
    </Tabs>

    <Tip>
      Not sure which connector to use? The [Identity Fabric overview](/reference/orchestrator/identity-fabric) includes a comparison table mapping each connector to its supported protocols and use cases.
    </Tip>

    <Note>
      **Protocol translation:** The Orchestrator's mode (LDAP Provider) determines what protocol your *application* speaks. The connector determines what protocol your *IdP* speaks. These are independent -- you can connect an OIDC-only or SAML-only IdP and the Orchestrator (through your Service Extensions) will translate identity data into LDAP responses for your application. See the [Identity Fabric overview](/reference/orchestrator/identity-fabric) for all connector types.
    </Note>
  </Step>

  <Step title="Write the authentication Service Extension">
    The authentication Service Extension handles LDAP bind operations. When an LDAP-dependent application sends a bind request (DN + password), this Service Extension validates the credentials against your upstream identity provider and returns success or failure.

    This step is **unique to the LDAP Provider** -- other Orchestrator modes handle authentication declaratively. The LDAP Provider requires a Go Service Extension because LDAP bind semantics vary widely across applications, and the translation from LDAP bind to cloud IdP authentication (typically via OIDC Resource Owner Password Credentials grant or a direct API call) requires custom logic.

    Your authentication Service Extension should:

    1. Receive the bind DN and password from the LDAP client
    2. Parse the bind DN to extract the username or identifier
    3. Translate the credentials into an authentication request against the upstream IdP (e.g., ROPC grant, SCIM API, or direct API call)
    4. Return success or failure to the LDAP client

    See the [Service Extensions reference](/reference/orchestrator/service-extensions) for the function signature, available context objects, and build instructions.

    <Warning>
      **Illustrative example only.** The following Service Extension is a simplified test example with hardcoded credentials. A production implementation would validate credentials against your upstream identity provider (e.g., via ROPC grant or direct API call) and retrieve user data from `api.Session()` or `api.Cache()`. See the [Service Extensions reference](/reference/orchestrator/service-extensions) for production patterns.
    </Warning>

    ```go simple.go theme={null}
    import (
      "strings"

      "github.com/strata-io/service-extension/orchestrator"
    )

    // This is an example Simple Authentication Service Extension that shows a quick way
    // to test a Bind request using the Simple Authentication method between the
    // application and the Orchestrator. In this example, only a user with the username
    // of `bob` and the password of `th3Bu!ld3R` to be authenticated.
    //
    // A production version of this Service Extension would typically retrieve the
    // password hash of the user from either `api.Cache` or `api.Session` and would
    // compute the hash of the password received and compare it with the one stored. The
    // user is typically a short-lived / one-time user created by an Orchestrator serving
    // as the identity-aware proxy, after the end-user has been authenticated by
    // the configured IdP.
    func Authenticate(api orchestrator.Orchestrator, username string, password string) (bool, error) {
      api.Logger().Debug(
        "service", "LDAP Provider",
        "extension", "Authenticate",
        "msg", "authenticating user",
        "username", username,
      )

      if strings.EqualFold(username, "bob") && password == "th3Bu!ld3R" {
        api.Logger().Debug(
          "service", "LDAP Provider",
          "extension", "Authenticate",
          "msg", "user authenticated",
          "username", username,
        )
        return true, nil
      }

      api.Logger().Debug(
        "service", "LDAP Provider",
        "extension", "Authenticate",
        "msg", "user not authenticated",
        "username", username,
      )
      return false, nil
    }
    ```

    <Tabs>
      <Tab title="Console UI">
        1. Open your deployment and click the **gear icon** to open **Deployment Settings**.
        2. In the **LDAP Provider** section, find the **Authentication** area.
        3. Toggle **Enable Simple Auth** to on.
        4. Under **Authentication Service Extension**, select your compiled Service Extension from the dropdown.
        5. Click **Save**.
      </Tab>

      <Tab title="Configuration">
        Configure the authentication Service Extension in the `ldapProvider.authentication` section.

        ```yaml maverics.yaml theme={null}
        ldapProvider:
          enabled: true
          uri: ldaps://0.0.0.0:636
          tls: ldap-server-tls
          authentication:
            allowAnonymous: false
            methods:
              simple:
                enabled: true
                authenticateSE:
                  file: /etc/maverics/extensions/authenticate.go
                  funcName: Authenticate
        ```

        The `authenticateSE.file` points to the Go source file containing your authentication logic. The `authenticateSE.funcName` specifies the entry point function that the Orchestrator calls when it receives a bind request.
      </Tab>
    </Tabs>
  </Step>

  <Step title="Write the search Service Extension">
    The search Service Extension handles LDAP search operations. When an LDAP-dependent application sends a search request (base DN, scope, filter, requested attributes), this Service Extension translates the query into lookups against your upstream identity sources and returns matching entries in LDAP format.

    Your search Service Extension should:

    1. Receive the search parameters (base DN, scope, filter, requested attributes) from the LDAP client
    2. Parse the LDAP filter to determine what the application is looking for
    3. Query the upstream identity source (cloud IdP API, database, etc.) for matching users or groups
    4. Map the upstream attributes into the LDAP attribute names the application expects (e.g., `cn`, `sAMAccountName`, `memberOf`, `mail`)
    5. Return the matching entries in LDAP format

    See the [Service Extensions reference](/reference/orchestrator/service-extensions) for the function signature, available context objects, and build instructions.

    <Warning>
      **Illustrative example only.** The following Service Extension returns hardcoded attributes for a test user. A production implementation would query your upstream identity provider or attribute source for real user data, typically via `api.Session()`, `api.Cache()`, or `api.AttributeProvider()`. See the [Service Extensions reference](/reference/orchestrator/service-extensions) for production patterns.
    </Warning>

    ```go search.go theme={null}
    import (
      "strings"

      "github.com/strata-io/service-extension/ldap"
      "github.com/strata-io/service-extension/orchestrator"
    )

    // This is an example of an Search Service Extension and shows a simple way to verify
    // the LDAP connection between the application and the Orchestrator. It is configured
    // to return hard-coded attributes for a test user, regardless of the Search request
    // received.
    //
    // A production version will be dependant on the application and environment; however,
    // typically, attributes originate from the end-users Session and would NOT be
    // hardcoded. For example, if the policy requires a user to be authenticated
    // using Entra ID, then the attributes would likely come from Entra ID, and stored
    // on the cache via the Orchestrator servicing as the proxy for the application.
    func Search(api orchestrator.Orchestrator, dn string, filter string, reqAttrs []string) (map[string]map[string]interface{}, error) {
      api.Logger().Debug(
        "service", "LDAP Provider",
        "extension", "Search",
        "msg", "processing request",
        "user", ldap.BindDN(api.Context()),
        "dn", dn,
        "filter", filter,
        "attributes", strings.Join(reqAttrs, ","),
      )

      result := make(map[string]map[string]interface{})

      // User attributes would typically originate from the users Session and would NOT
      // be hardcoded. For example, if the policy requires a user to be authenticated
      // using Entra ID, then the attributes would likely come from Entra ID.
      userAttrs := make(map[string]interface{})
      userAttrs["Name"] = "Test User"
      userAttrs["mail"] = "test.user@acme.org"
      userAttrs["givenName"] = "Test"
      userAttrs["sn"] = "User"

      result["CN=Test,CN=Domain Users,CN=Users,DC=acme,DC=local"] = userAttrs

      api.Logger().Debug(
        "service", "LDAP Provider",
        "extension", "Search",
        "msg", "request processed",
        "dn", dn,
        "filter", filter,
        "attributes", strings.Join(reqAttrs, ","),
      )

      return result, nil
    }
    ```

    <Tabs>
      <Tab title="Console UI">
        1. Open your deployment and click the **gear icon** to open **Deployment Settings**.
        2. In the **LDAP Provider** section, find the **Search** area.
        3. Under **Search Service Extension**, select your compiled Service Extension from the dropdown.
        4. Click **Save**.
      </Tab>

      <Tab title="Configuration">
        Configure the search Service Extension in the `ldapProvider.search` section.

        ```yaml maverics.yaml theme={null}
        ldapProvider:
          enabled: true
          uri: ldaps://0.0.0.0:636
          tls: ldap-server-tls
          search:
            searchSE:
              file: /etc/maverics/extensions/search.go
              funcName: Search
        ```

        The `searchSE.file` points to the Go source file containing your search logic. The `searchSE.funcName` specifies the entry point function that the Orchestrator calls when it receives a search request.
      </Tab>
    </Tabs>
  </Step>

  <Step title="Configure the facade pattern (optional)">
    The most common LDAP Provider deployment pairs it with [HTTP Proxy](/reference/modes/http-proxy) mode in a **facade** (or "sandwich") pattern. In this architecture, the application sits between two Orchestrator roles:

    * **HTTP Proxy** handles browser-facing authentication -- intercepting HTTP requests, redirecting users to a cloud IdP for modern authentication (MFA, conditional access), and injecting identity headers or stuffing login forms
    * **LDAP Provider** handles the application's backend LDAP operations -- authenticating bind requests and serving search results from the same cloud IdP

    This pattern lets you modernize the full authentication stack for LDAP-dependent applications without changing any application code.

    **To set up the facade pattern:**

    1. **Configure an HTTP Proxy application** for the web tier -- follow the [HTTP Proxy Auth guide](/guides/authentication/http-proxy-auth) to set up reverse proxy authentication with header injection or form-stuffing.
    2. **Configure the LDAP Provider** (this guide) for the backend tier -- the same Orchestrator instance or a separate one handles the application's LDAP bind and search requests.
    3. **Add a shared cache** if running separate Orchestrator instances -- when the HTTP Proxy and LDAP Provider run as separate Orchestrator instances (the most common facade deployment), they need a [shared cache](/reference/orchestrator/caches) so that one-time credentials generated by the proxy Orchestrator can be validated by the LDAP Provider Orchestrator.

    <Info>
      The facade can be deployed as a single Orchestrator running both HTTP Proxy and LDAP Provider modes, two separate Orchestrator instances, or groups of Orchestrators for high-availability deployments. When both modes run on a single Orchestrator instance, the local in-memory cache is sufficient.
    </Info>

    See the [LDAP Provider reference -- Pairing with HTTP Proxy](/reference/modes/ldap-provider#pairing-with-http-proxy) for the complete architecture diagram, credential delivery methods, and shared cache requirements.
  </Step>

  <Step title="Publish and test">
    With the LDAP Provider enabled, identity connector configured, and Service Extensions in place, you are ready to publish and test the virtual LDAP directory.

    <Tabs>
      <Tab title="Console UI">
        1. At the bottom of the deployment Settings page, click **Publish Preview** in the sticky footer bar.
        2. In the Deployment Manager dialog, review the configuration diff to verify your changes.
        3. Optionally add a revision note describing what changed (e.g., "Initial LDAP Provider setup").
        4. Click **Publish** to deploy the configuration bundle to your storage provider.
        5. The Orchestrator detects the new bundle on its next poll cycle and applies the configuration automatically.

        See [Publishing Deployment Configs overview](/reference/console/config-publishing) for details on the publishing lifecycle, revision history, and bundle verification.
      </Tab>

      <Tab title="Configuration">
        When using direct configuration, start (or restart) the Orchestrator to apply your changes:

        ```bash theme={null}
        maverics -config /etc/maverics/config.yaml
        ```
      </Tab>
    </Tabs>

    **Test the LDAP connection:**

    Point your LDAP client application at the Orchestrator's LDAP endpoint and verify the full flow:

    1. **Test connectivity** -- Use an LDAP client tool (such as `ldapsearch` or Apache Directory Studio) to connect to the Orchestrator's LDAP URI and verify the connection is accepted.

    ```bash theme={null}
    ldapsearch -H ldaps://your-orchestrator.example.com:636 -x -b "dc=example,dc=com" -s base "(objectClass=*)"
    ```

    2. **Test bind authentication** -- Attempt a bind operation with valid credentials to verify the authentication Service Extension is working.

    ```bash theme={null}
    ldapsearch -H ldaps://your-orchestrator.example.com:636 -x -D "cn=user@example.com,dc=example,dc=com" -W -b "dc=example,dc=com" "(cn=user@example.com)"
    ```

    3. **Test search** -- After a successful bind, perform a search to verify the search Service Extension returns the expected attributes.

    4. **Test with your application** -- Point your LDAP-dependent application at the Orchestrator's LDAP endpoint and walk through a full authentication and data retrieval flow.

    <Check>
      **Success!** Your Maverics Orchestrator is acting as a virtual LDAP directory --
      authenticating LDAP bind requests against your cloud identity provider and
      serving directory data to your LDAP-dependent application. The application
      continues using its existing LDAP configuration while the actual identity
      data comes from your modern cloud IdP.
    </Check>
  </Step>
</Steps>

## What's Next

<CardGroup cols={2}>
  <Card title="LDAP Provider Reference" icon="folder-tree" href="/reference/modes/ldap-provider">
    Complete configuration reference for LDAP Provider mode -- authentication methods, search handling, Root DSE, and SASL/NTLM
  </Card>

  <Card title="Add SSO to Web Apps" icon="server" href="/guides/authentication/http-proxy-auth">
    Deploy the Orchestrator as a reverse proxy -- pair with LDAP Provider for the facade pattern
  </Card>

  <Card title="Service Extensions" icon="code" href="/reference/orchestrator/service-extensions">
    Write Go-based Service Extensions for custom authentication, search, and credential handling logic
  </Card>

  <Card title="Identity Fabric" icon="plug" href="/reference/orchestrator/identity-fabric">
    Connect upstream identity providers to the Orchestrator -- Microsoft Entra ID, Okta, PingFederate, and more
  </Card>
</CardGroup>
