Limited Availability
The LDAP provider is not enabled by default. If you would like to enable this on your account please contact support@strata.io.
LDAP Provider enables applications that use LDAP for user authentication to take advantage of modern authentication (e.g., OIDC) without rewriting the application.
A deployment pattern known as a facade is typically used to update and modernize LDAP-based applications. When an application uses this pattern, it utilizes an orchestrator acting as an identity-aware proxy (Proxy app) and an orchestrator acting as an LDAP server (LDAP Provider) and sandwiches the application between the orchestrators.
During implementation, the LDAP configuration for the application is updated to point to the orchestrator, acting as the LDAP server instead of the currently configured LDAP server (e.g., Active Directory). This configuration change is the only update required on the application server, no application code changes are needed.
Using this pattern, end-user requests flow through the orchestrator configured as an app proxy, so they can be authenticated using your preferred IDPs, support resilience, and be authorized based on the configured policy.
Once the user is authenticated, the orchestrator uses the configured service extensions to log into the application behind the scenes, so the end-user never sees the native application login page. The application typically uses the credentials received and authenticates them via an LDAP Bind request. When the orchestrator receives this Bind request, it authenticates the user based on the type of Bind request (e.g., Simple, SASL) received and how the orchestrator has been configured.
For instance, the orchestrator acting as an identity-aware proxy can:
Create a one-time user.
Pass this one-time user to the application being protected.
The application will send a Bind request using these credentials.
The orchestrator acting as an LDAP server authenticates the request using the data stored about the one-time user (e.g., hashed one-time password).
Once authenticated, the application typically sends a Search request to the LDAP Provider asking for the attributes of the authenticated user (e.g., employee ID, email, name). Upon receiving the response, the application finishes performing any required actions to show the user the requested page.
This deployment pattern supports many applications requiring LDAP and can easily be fine-tuned for your environment. Additionally, while we mention that this can be deployed on two separate orchestrators, one acting as an identity-aware proxy and one acting as an LDAP server, these can be the same orchestrator, or groups of orchestrators to support Highly Available (HA) deployments.
.png?sv=2022-11-02&spr=https&st=2025-06-07T20%3A19%3A51Z&se=2025-06-07T20%3A36%3A51Z&sr=c&sp=r&sig=E0wOJFPB72f4rGm7%2Ft%2BwBfhztixWSlY22BGum%2FLPAgQ%3D)
Click to enlarge
The LDAP provider is different from the LDAP connector which acts as a client to query external LDAP sources.
Supported Features
Only LDAP v3 is supported.
LDAP Operations
Simple
Simple authentication consists of sending the LDAP server the fully qualified DN of the client and the client's clear-text password (RFC 2251 and RFC 2829). Given that the password is not protected and can be easily read from the network, it is recommended that the connection use TLS.
See the RFC for more information.
Search
For Search requests, the connection MUST be authenticated. When received, the processing is deferred to the Search
Service Extension to consolidate and filter data sources and return the results to the client.
See the specification for more information
Configuration

Click to enlarge
Name | Description | Field |
---|---|---|
Enabled | Whether or not the LDAP Provider is enabled. This toggle must be turned on for the LDAP Provider to start. | |
URI | The address that the LDAP Provider listens on. It MUST begin with either | Examples below:
|
TLS Certificate (Optional) | The file path to your TLS certificate. | |
TLS Key File (Optional) | The file path to your TLS key. | |
Search Service Extension | The service extension that is invoked when a non-Root DSE search is received. While this is optional, for most applications, this service extension will be required. | |
Allow Anonymous | Whether the LDAP Provider allows anonymous authentication when receiving an LDAP Search. | |
Enable Simple Auth | Configure the orchestrator to act as an LDAP identity provider. These settings define how the orchestrator issues LDAP assertions, handles authentication and logout requests, and manages key material for signing and encryption. | |
Authentication Service Extension (Optional) | The required service extension that is invoked when a Bind request is received using simple authentication. Required when Simple Authentication is enabled. | Configure the Authentication service extension and select it here. |
As the author of a service extension you are responsible for its behavior.
Service Extension Examples
These examples are for illustrative purposes only, and do not reflect what production versions should look like. Production versions will depend on the application being protected and its environment.
/etc/maverics/extensions/search.go
import (
"strings"
"github.com/strata-io/go-ntlm"
"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",
"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
}
/etc/maverics/extensions/simple.go
import (
"strings"
"github.com/strata-io/go-ntlm"
"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
}
/etc/maverics/extensions/gss_spnego_ntlm.go
import (
"fmt"
"github.com/strata-io/go-ntlm"
"github.com/strata-io/service-extension/orchestrator"
)
var (
// THIS IS FOR TESTING PURPOSES ONLY. Do not use this pattern in a production
// deployment.
testUsers = map[string][]byte{
fmt.Sprintf("%s:%s", ntlm.ToUnicode("acme"), ntlm.ToUnicode("user123")): ntlm.ToUnicode("abc123"),
}
)
// This is an example of an NTLM NTLMGetHashedCredentials Service Extension and shows
// a simple way to test users while verifying the LDAP connection between the
// application and the Orchestrator.
//
// A production version of this Service Extension would typically retrieve the
// NT and LM hashes of the user from either `api.Cache` or `api.Session`, which would
// typically be computed by an Orchestrator serving as the application proxy. The
// user is typically a short-lived / one-time user created after the end-user has
// been authenticated and authorized.
func NTLMGetHashedCredentials(
api orchestrator.Orchestrator,
user []byte,
domain []byte,
) (ntHash []byte, lmHash []byte, err error) {
api.Logger().Debug(
"service", "LDAP Provider",
"extension", "NTLMGetHashedCredentials",
"msg", "retrieving NT and LM hashes for user",
"domain", fmt.Sprintf("%q", user[:]),
"user", fmt.Sprintf("%q", user[:]),
)
key := fmt.Sprintf("%s:%s", string(domain), string(user))
pass, ok := testUsers[key]
if !ok {
api.Logger().Debug(
"service", "LDAP Provider",
"extension", "NTLMGetHashedCredentials",
"msg", "invalid user",
"domain", fmt.Sprintf("%q", domain[:]),
"user", fmt.Sprintf("%q", user[:]),
)
return nil, nil, fmt.Errorf("invalid user: %s", key)
}
ntHash = ntlm.NTOWFv2(pass, user, domain)
lmHash = ntlm.LMOWFv2(pass, user, domain)
api.Logger().Debug(
"service", "LDAP Provider",
"extension", "NTLMGetHashedCredentials",
"msg", "NT and LM hashes found for user",
"domain", fmt.Sprintf("%q", domain[:]),
"user", fmt.Sprintf("%q", user[:]),
)
return ntHash, lmHash, err
}