Skip to main content

litestar-api-auth

Pluggable API key authentication for Litestar applications

PyPI version Python versions License Documentation

Features

  • Secure Key Generation: API key generation with SHA-256 hashing
  • Configurable Prefixes: Customizable key prefixes (e.g., pyorg_, myapp_)
  • Key Lifecycle Management: Expiration and revocation support
  • Usage Tracking: Last-used timestamp tracking for keys
  • Pluggable Backends: SQLAlchemy, Redis, and in-memory storage backends
  • Route Protection: Pre-built guards for securing endpoints
  • Auto-Registration: Automatic management route registration
  • Scopes & Permissions: Fine-grained key scopes and permissions system
  • OpenAPI Integration: Automatic OpenAPI schema generation

Installation

# Using uv (recommended)
uv add litestar-api-auth

# Using pip
pip install litestar-api-auth

With Optional Dependencies

# With SQLAlchemy support
uv add litestar-api-auth[sqlalchemy]

# With Redis support
uv add litestar-api-auth[redis]

# All optional dependencies
uv add litestar-api-auth[all]

Quick Start

Basic Configuration

from litestar import Litestar
from litestar_api_auth import APIAuthPlugin, APIAuthConfig
from litestar_api_auth.backends.memory import MemoryBackend

app = Litestar(
    plugins=[
        APIAuthPlugin(
            config=APIAuthConfig(
                backend=MemoryBackend(),  # Use SQLAlchemyBackend for production
                key_prefix="myapp_",
                header_name="X-API-Key",
                auto_routes=True,
                route_prefix="/api/v1/api-keys",
            )
        )
    ]
)

The auto-registered management routes (create/list/get/revoke/delete under route_prefix) require the api_keys:admin scope by default -- they let a caller mint keys with arbitrary scopes, so they must never be open to anonymous or low-privilege callers. Since a fresh deployment has no keys yet, seed the first admin key out-of-band before relying on the API, e.g.:

from litestar_api_auth import mint_api_key

raw_key, key_info = await mint_api_key(
    backend,
    name="bootstrap admin",
    scopes=["api_keys:admin"],
    prefix="myapp_",
)
# Store `raw_key` securely -- it is never retrievable again.

mint_api_key() is the single entry point for minting a key: it generates the key, derives the key_id, builds the APIKeyInfo record and persists it via backend.create(). Application code should never assemble the hash or the struct by hand -- that is how a caller ends up hashing with a scheme the middleware does not verify against.

To use a different policy, pass management_guards=[...] to APIAuthConfig.

Bearer Tokens

Set auth_scheme to accept a scheme-prefixed header such as Authorization: Bearer <key>:

APIAuthConfig(
    backend=backend,
    header_name="Authorization",
    auth_scheme="Bearer",
)

The prefix match is case-insensitive, and a header value without the prefix is ignored entirely rather than treated as a key -- so an unrelated credential sharing the same header (Basic ..., a JWT under another scheme) can never authenticate. The generated OpenAPI schema documents this as an HTTP bearer scheme instead of an apiKey header. Leaving auth_scheme unset (the default) keeps the plain X-API-Key behavior, where the whole header value is the key.

Protecting Routes with Guards

from litestar import get
from litestar_api_auth import require_api_key, require_scope

@get("/protected", guards=[require_api_key])
async def protected_route() -> dict:
    """Requires any valid API key."""
    return {"status": "authenticated"}

@get("/admin", guards=[require_scope("admin:write")])
async def admin_route() -> dict:
    """Requires an API key with the 'admin:write' scope."""
    return {"status": "admin access"}

Working with API Keys

from datetime import datetime, timedelta, timezone
from litestar_api_auth.types import APIKeyInfo

# `APIKeyInfo` is the one record type the library uses: backends store it,
# the middleware puts it in `request.state.api_key`, and guards read it.
# `litestar_api_auth.types.APIKeyInfo` and
# `litestar_api_auth.backends.base.APIKeyInfo` are the same class.
key_info = APIKeyInfo(
    key_id="abc123",
    name="Production API Key",
    scopes=["read:users", "write:posts"],
    key_hash="...",  # sensitive: the stored verifier
    prefix="myapp_",
    created_at=datetime.now(timezone.utc),
    expires_at=datetime.now(timezone.utc) + timedelta(days=365),
    last_used_at=None,
    is_active=True,
    metadata={"owner": "admin@example.com"},
)

# Check key validity
if key_info.is_valid:
    print("Key is active and not expired")

# Check for specific scopes
if key_info.has_scope("read:users"):
    print("Key has read:users scope")

# Check for multiple scopes
if key_info.has_scopes(["read:users", "write:users"], requirement="all"):
    print("Key has all required scopes")

Key States

API keys can be in one of three states:

State Description
ACTIVE Key is active and can be used for authentication
EXPIRED Key has passed its expiration date
REVOKED Key has been manually revoked

Storage Backends

SQLAlchemy Backend

from sqlalchemy.ext.asyncio import create_async_engine
from litestar_api_auth.backends.sqlalchemy import SQLAlchemyBackend, SQLAlchemyConfig

engine = create_async_engine("postgresql+asyncpg://user:pass@localhost/db")
backend = SQLAlchemyBackend(config=SQLAlchemyConfig(engine=engine))

If the engine is owned and shared by the host application, pass dispose_engine=False so closing the backend on shutdown does not tear down connections the rest of the application still uses:

backend = SQLAlchemyBackend(
    config=SQLAlchemyConfig(engine=app_engine, dispose_engine=False)
)

Redis Backend

from litestar_api_auth.backends.redis import RedisBackend

backend = RedisBackend(url="redis://localhost:6379/0")

In-Memory Backend (Testing)

from litestar_api_auth.backends.memory import MemoryBackend

backend = MemoryBackend()

Error Handling

The library provides a comprehensive exception hierarchy:

from litestar_api_auth.exceptions import (
    APIAuthError,           # Base exception for all auth errors
    APIKeyNotFoundError,    # Key does not exist
    APIKeyExpiredError,     # Key has expired
    APIKeyRevokedError,     # Key has been revoked
    InsufficientScopesError, # Key lacks required scopes
    InvalidAPIKeyError,     # Key format is invalid
    ConfigurationError,     # Plugin misconfiguration
)

Example Error Handling

from litestar import get
from litestar.exceptions import HTTPException
from litestar_api_auth.exceptions import (
    APIKeyExpiredError,
    InsufficientScopesError,
)

@get("/resource")
async def get_resource() -> dict:
    try:
        # ... authentication logic
        pass
    except APIKeyExpiredError as e:
        raise HTTPException(status_code=401, detail="API key has expired")
    except InsufficientScopesError as e:
        raise HTTPException(
            status_code=403,
            detail=f"Missing required scopes: {e.required_scopes}"
        )

Configuration Options

Option Type Default Description
backend APIKeyBackend Required Storage backend instance
key_prefix str "pyorg_" Prefix for generated keys
header_name str "X-API-Key" HTTP header name for API key
auth_scheme str | None None Scheme prefix required on the header value, e.g. "Bearer". None means the whole value is the key
auto_routes bool True Auto-register management routes
route_prefix str "/api-keys" Prefix for management routes
management_guards list[Guard] requires api_keys:admin scope Guards applied to auto-registered management routes
enable_openapi bool True Include auth in OpenAPI schema
openapi_global_security bool False Also advertise the scheme as a document-wide security requirement (marks every route as key-authed)
track_usage bool True Update last_used_at on requests

Documentation

Full documentation is available at https://jacobcoffee.github.io/litestar-api-auth

Contributing

Contributions are welcome! Please see our Contributing Guide for details.

License

MIT License - see LICENSE for details.

Metadata

Release files for litestar-api-auth 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for litestar-api-auth 0.1.1
File Size Uploaded
litestar_api_auth-0.1.1.tar.gz 53.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for litestar-api-auth 0.1.1
File Interpreter ABI Platform
litestar_api_auth-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 114.3 kB

Release files / litestar_api_auth-0.1.1.tar.gz

Download URL litestar_api_auth-0.1.1.tar.gz
Size 53.4 kB
Tags Source
SHA-256 checksum
How to use checksums
846bb44ee404685c56d052806a6757c09e75272bec9c354d44e0b9f158a7ed34
BLAKE2b-256 checksum
How to use checksums
4dc2ce2ade1302845ce3875e9858dbb0d5c34666d9949872ed287ff1f7d902b4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.

Transparency log

Release files / litestar_api_auth-0.1.1-py3-none-any.whl

Download URL litestar_api_auth-0.1.1-py3-none-any.whl
Size 60.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
88f988051a13e6a4e9874f89ced62e50eba192a088feb64dd083fd23bc5186e6
BLAKE2b-256 checksum
How to use checksums
54889558ddada7e20d9a0e27b613d963ddef9b053468057e4b9bb4a1a0f9092d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.

Transparency log

Release history Release notifications | RSS feed

0.1.2

2 release files

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page