Lightweight LDAP/AD authentication proxy and FastAPI middleware
Project description
LDAPGate
Lightweight LDAP/AD authentication gateway for Python web apps. Install it, configure it, done.
Features
- Two deployment modes — standalone reverse proxy or drop-in FastAPI middleware
- WebDAV + browser in one — browsers get a login form; WebDAV clients (Windows, macOS Finder, curl) get a Basic auth challenge — same endpoint, no extra config
- Pure Python LDAP — no OS-level libs required, uses
ldap3 - Signed cookie sessions — stateless, no server-side session storage
- OpenLDAP and Active Directory —
uid=andsAMAccountName=out of the box - Optional group gating — restrict access to members of a specific LDAP group
- Header injection — injects
X-Forwarded-Userfor downstream apps - Bundled login form — responsive, dark/light mode, customisable, works air-gapped
Install
pip install ldapgate
Config file
Both modes share the same ldapgate.yaml:
ldap:
url: ldaps://dc.example.com:636
bind_dn: CN=svc,CN=Users,DC=example,DC=com
bind_password: secret
base_dn: DC=example,DC=com
user_filter: "(sAMAccountName={username})" # AD; OpenLDAP: (uid={username})
group_dn: CN=app-users,CN=Users,DC=example,DC=com # optional — restrict by group
allowed_users: # optional — local allowlist
- alice
- bob
timeout: 10
tls_validate: REQUIRED # NONE | OPTIONAL | REQUIRED
tls_ca_cert_file: /etc/ssl/certs/internal-ca.pem # optional — custom CA bundle
tls_client_cert_file: /etc/ssl/certs/client.pem # optional — mutual TLS client cert
tls_client_key_file: /etc/ssl/private/client.key # optional — mutual TLS client key
proxy:
listen_host: 0.0.0.0
listen_port: 9000
backend_url: http://localhost:8080
secret_key: change-me-to-something-random
session_ttl: 3600
user_header: X-Forwarded-User
login_path: /_auth/login
logout_path: /_auth/logout
app_name: MyApp
secure_cookies: false # set true when behind HTTPS
All settings can also be provided via environment variables using __ as a separator — e.g. LDAP__URL, PROXY__SECRET_KEY.
Corporate / Active Directory setup
For corp environments with internal CAs where cert validation isn't feasible:
ldap:
url: ldaps://dc.example.com:636
tls_validate: NONE
# ... other settings ...
For plain LDAP with STARTTLS:
ldap:
url: ldap://dc.example.com:389
use_starttls: true
Mode 1 — Standalone Reverse Proxy
Run ldapgate as a standalone process in front of any app.
Browser / WebDAV client → ldapgate :9000 → backend app :8080
ldapgate serve --config ldapgate.yaml
All traffic is intercepted by ldapgate before reaching the backend. Authenticated requests are forwarded with the X-Forwarded-User header set to the verified username. Apps can point their logout link at the configured logout_path (default /_auth/logout) to clear the session.
WebDAV clients receive a 401 WWW-Authenticate: Basic challenge automatically and authenticate per-request via HTTP Basic auth — no session cookie needed.
Mode 2 — FastAPI Middleware
Drop ldapgate auth directly into an existing FastAPI app — no separate process.
from fastapi import FastAPI
from ldapgate.config import load_config
from ldapgate.middleware import add_ldap_auth
app = FastAPI()
config = load_config("ldapgate.yaml")
add_ldap_auth(app, config)
@app.get("/api/data")
async def data(request):
return {"user": request.state.user} # authenticated username
add_ldap_auth registers the login/logout routes and attaches the middleware in one call. The authenticated username is available as request.state.user and is also injected as the configured user_header into the request headers.
WebDAV with middleware
The middleware handles both browser and WebDAV clients on the same app instance:
| Client | Auth flow |
|---|---|
| Browser | Redirected to login form → session cookie |
| WebDAV (Windows, macOS Finder, curl) | 401 WWW-Authenticate: Basic challenge → Basic auth per-request |
No extra routes or config needed — if a request arrives without a session cookie and without Accept: text/html, the middleware issues a 401 with a WWW-Authenticate: Basic header. The client sends credentials, the middleware validates against LDAP, and the request proceeds.
allowed_users and group_dn apply to both flows — a user blocked by those settings is rejected regardless of whether they authenticated via cookie or Basic auth.
Example — xwing file server with WebDAV:
from fastapi import FastAPI
from ldapgate.config import load_config
from ldapgate.middleware import add_ldap_auth
from xwing.app import create_app
from xwing.config import Settings
xwing_settings = Settings(root_dir="/srv/files", users_config="users.yaml")
app = create_app(xwing_settings)
ldap_config = load_config("ldapgate.yaml")
add_ldap_auth(app, ldap_config)
Windows users can now map http://your-server:8989/ as a network drive:
net use Z: http://your-server:8989/ /user:alice /persistent:yes
macOS Finder: Go → Connect to Server (⌘K) → http://your-server:8989/
CLI reference
ldapgate serve [OPTIONS]
--config PATH Path to ldapgate.yaml (reads env vars if omitted)
--host TEXT Override listen host
--port INTEGER Override listen port
--backend TEXT Override backend URL
--reload Enable auto-reload (dev only)
Development
Requires uv.
git clone https://github.com/anudeepd/ldapgate
cd ldapgate
uv sync
uv run pytest
License
MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ldapgate-0.1.4.tar.gz.
File metadata
- Download URL: ldapgate-0.1.4.tar.gz
- Upload date:
- Size: 862.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.12 {"installer":{"name":"uv","version":"0.10.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"CachyOS Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f1467de1c8e1e7e9c8f9664b9f7989b0583682fd2ad8eadd107a527e7e48537c
|
|
| MD5 |
cc6f36c1300df4da270aa4ab966bfc95
|
|
| BLAKE2b-256 |
89121227c4ede67b1b81d59c80463f8d1bc5f50dcac84003fb0b41d62b09971e
|
File details
Details for the file ldapgate-0.1.4-py3-none-any.whl.
File metadata
- Download URL: ldapgate-0.1.4-py3-none-any.whl
- Upload date:
- Size: 114.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.12 {"installer":{"name":"uv","version":"0.10.12","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"CachyOS Linux","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
918735cd12f5bf8618aa07ebda786c7492cba19eeebda916babbf691e6516285
|
|
| MD5 |
701d5ec6c4694cd041f8c454b1361d60
|
|
| BLAKE2b-256 |
89074c26a6e2b5fa4f3515492754e99c924950a1bc67d052fb73a9973043f263
|