Skip to main content

Production-ready LDAP authentication and authorization for FastAPI

Project description

codecov

fastapi-ldap

Production-ready LDAP authentication and authorization for FastAPI.

Features

  • Async-first design - No blocking LDAP calls in the event loop
  • Security by default - TLS enabled, no anonymous binds, fail-closed behavior
  • Enterprise-ready - LDAP and Active Directory compatible
  • Kubernetes-friendly - Health and readiness checks included
  • FastAPI-native - Idiomatic dependencies and lifespan management
  • Optional caching - Configurable TTL for authentication results
  • Type-safe - Full type hints, Python 3.10+

Installation

pip install fastapi-ldap

Quick Start

from fastapi import Depends, FastAPI
from fastapi_ldap import (
    LDAPAuth,
    LDAPSettings,
    get_current_user,
    health_check,
    readiness_check,
    require_groups,
)
from fastapi_ldap.models import LDAPUser

# Configure LDAP settings
settings = LDAPSettings(
    ldap_url="ldaps://ldap.example.com:636",
    ldap_base_dn="dc=example,dc=com",
    bind_dn="cn=admin,dc=example,dc=com",
    bind_password="secret",
)

# Create LDAP auth instance
ldap_auth = LDAPAuth(settings)

# Create FastAPI app with lifespan
app = FastAPI(lifespan=ldap_auth.lifespan)

# Health checks
@app.get("/health")
async def health():
    return await health_check()

@app.get("/ready")
async def ready():
    return await readiness_check()

# Protected route
@app.get("/protected")
async def protected_route(user: LDAPUser = Depends(get_current_user)):
    return {
        "username": user.username,
        "email": user.email,
        "groups": list(user.groups),
    }

# Route requiring specific groups
@app.get("/admin")
async def admin_route(
    user: LDAPUser = Depends(require_groups("admins", "superusers"))
):
    return {"message": f"Welcome, {user.username}!"}

Configuration

Configuration can be provided via environment variables (prefixed with LDAP_) or directly:

from fastapi_ldap import LDAPSettings

settings = LDAPSettings(
    # Required
    ldap_url="ldaps://ldap.example.com:636",
    ldap_base_dn="dc=example,dc=com",
    
    # Bind credentials (required unless allow_anonymous=True)
    bind_dn="cn=admin,dc=example,dc=com",
    bind_password="secret",
    
    # TLS (enabled by default)
    use_tls=True,
    tls_ca_cert_file="/path/to/ca.crt",
    
    # Search configuration
    user_search_filter="(uid={username})",  # Default for OpenLDAP
    # user_search_filter="(sAMAccountName={username})",  # For Active Directory
    
    # Connection pool
    pool_size=10,
    pool_timeout=30.0,
    
    # Caching (optional)
    cache_enabled=True,
    cache_ttl=300,  # 5 minutes
)

Environment Variables

All settings can be configured via environment variables:

export LDAP_URL="ldaps://ldap.example.com:636"
export LDAP_BASE_DN="dc=example,dc=com"
export LDAP_BIND_DN="cn=admin,dc=example,dc=com"
export LDAP_BIND_PASSWORD="secret"
export LDAP_CACHE_ENABLED="true"
export LDAP_CACHE_TTL="300"

Important: Passwords with Special Characters

If your password contains special characters (e.g., $, %, &, *, (, ), \, etc.), make sure to properly quote the environment variable to prevent shell interpretation:

# ✅ Correct - use single quotes to prevent shell interpretation
export LDAP_BIND_PASSWORD='pass$word%test&more'

# ✅ Also correct - use double quotes and escape special chars
export LDAP_BIND_PASSWORD="pass\$word%test&more"

# ❌ Wrong - shell will interpret $word as a variable
export LDAP_BIND_PASSWORD=pass$word

Note: LDAP passwords can contain any characters, including special ones and Unicode. The fastapi-ldap library accepts passwords as-is without validation, as LDAP servers handle password validation. The only requirement is that passwords are properly set in environment variables to avoid shell interpretation issues.

Active Directory Example

settings = LDAPSettings(
    ldap_url="ldaps://ad.example.com:636",
    ldap_base_dn="dc=example,dc=com",
    bind_dn="CN=Service Account,CN=Users,DC=example,DC=com",
    bind_password="password",
    user_search_filter="(sAMAccountName={username})",
    user_search_base="CN=Users,DC=example,DC=com",
    group_search_filter="(member:1.2.840.113556.1.4.1941:={user_dn})",  # Recursive group search
    group_attribute="cn",
)

API Reference

Dependencies

  • get_current_user - Get the current authenticated user (HTTP Basic Auth)
  • require_groups(*groups) - Require user to belong to at least one of the specified groups
  • require_roles(*roles) - Alias for require_groups (semantic clarity)

Models

  • LDAPUser - Immutable user object with:
    • dn - Distinguished Name
    • username - Username
    • email - Email address (optional)
    • display_name - Display name (optional)
    • groups - Frozen set of group names
    • attributes - Additional LDAP attributes
    • has_group(group) - Check if user belongs to a group
    • has_any_group(groups) - Check if user belongs to any group
    • has_all_groups(groups) - Check if user belongs to all groups

Health Checks

  • health_check() - Basic health check (does not verify LDAP)
  • readiness_check() - Verifies LDAP connectivity (for Kubernetes probes)

Security Considerations

  • TLS is enabled by default - Disable only for testing in secure environments
  • Anonymous binds are disabled by default - Must be explicitly enabled
  • Authentication failures are indistinguishable - Prevents user enumeration
  • No credential logging - Passwords are never logged
  • Fail-closed behavior - If LDAP is unavailable, authentication fails securely

Architecture

The module follows a layered architecture:

  1. LDAP Client Layer (client.py) - Isolated from FastAPI, handles connections, retries, timeouts, TLS
  2. Auth Layer (auth.py) - FastAPI dependencies, converts LDAP errors to HTTP errors
  3. Models (models.py) - Immutable LDAP user objects
  4. Cache Layer (cache.py) - Optional caching with configurable TTL
  5. Configuration (config.py) - Explicit, documented settings

Development

# Install development dependencies
pip install -e ".[dev]"

# Run tests
pytest

# Format code
black fastapi_ldap tests

# Lint code
ruff check fastapi_ldap tests

# Type check
mypy fastapi_ldap

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

fastapi_ldap-0.1.2.tar.gz (21.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fastapi_ldap-0.1.2-py3-none-any.whl (14.0 kB view details)

Uploaded Python 3

File details

Details for the file fastapi_ldap-0.1.2.tar.gz.

File metadata

  • Download URL: fastapi_ldap-0.1.2.tar.gz
  • Upload date:
  • Size: 21.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.4.1 CPython/3.14.5 Darwin/25.5.0

File hashes

Hashes for fastapi_ldap-0.1.2.tar.gz
Algorithm Hash digest
SHA256 d013fef59d57c4611a8c1ff3938651e96dfa07838838a218aa677f4589fcbd12
MD5 a42ef9298a7a5903c40f528adeab7642
BLAKE2b-256 6d33eaeea401aba058c790cfa367f3b26315f1714f2900c9d1536e6aaae21fed

See more details on using hashes here.

File details

Details for the file fastapi_ldap-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: fastapi_ldap-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 14.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/2.4.1 CPython/3.14.5 Darwin/25.5.0

File hashes

Hashes for fastapi_ldap-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 17035a8d4cf165f151db205dd579cf21676c7cc2196e01e61cd53fed9c82d749
MD5 d5e0fb2fe2f2872b97ed70254c167a6d
BLAKE2b-256 452608250f1b5827a940c1c0d142927dd55c9e344cac197e4b7e8dee637cd2e0

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page