Skip to main content

Auth Middleware

PyPI version Python 3.14+ License: MIT Coverage

Async Authentication and Authorization Middleware for FastAPI/Starlette Applications

Auth Middleware is a comprehensive, production-ready authentication and authorization solution for FastAPI and Starlette applications. It provides a pluggable architecture that supports multiple identity providers and authorization backends with async-first design principles.

Features

Authentication Providers

  • Generic OIDC — Any standards-compliant OpenID Connect provider — AWS Cognito, Authentik, Keycloak, Auth0, Okta, ... — via OidcProvider
  • Azure Entra ID — Microsoft Azure Active Directory authentication
  • Basic Auth — Username/password authentication via BasicAuthMiddleware
  • Custom Providers — Extensible architecture via the JWTProvider contract

Authorization & Access Control

  • Group-based Authorization — Role-based access control with user groups
  • Role-based Authorization — Fine-grained role system
  • Permission-based Authorization — Fine-grained permission system
  • SQL Backend Support — PostgreSQL and MySQL for groups/permissions storage via SQLAlchemy
  • Cognito Groups Integration — Direct integration with AWS Cognito groups (cognito:groups claim)
  • Cognito Groups-as-Roles — Map Cognito groups directly to roles
  • Custom Authorization Providers — Build your own via the GroupsProvider, RolesProvider and PermissionsProvider contracts

Performance & Reliability

  • Async-First Design — Built for high-performance async applications
  • JWKS Caching — Intelligent caching of JSON Web Key Sets
  • Connection Pooling — Efficient database and HTTP connections
  • Lazy Loading — User groups and permissions loaded on-demand
  • Error Resilience — Graceful degradation on provider failures

Developer Experience

  • Type-Safe — Full type hints throughout, compatible with mypy strict mode
  • FastAPI Integration — Native dependency injection support
  • Middleware Pattern — Standard ASGI middleware implementation
  • Environment Configuration — 12-factor app configuration support
  • Comprehensive Documentation — Detailed guides and API reference

Installation

# pip
pip install auth-middleware

# poetry
poetry add auth-middleware

# uv (recommended)
uv add auth-middleware

Quick Start

JWT Authentication with AWS Cognito (via generic OIDC)

from fastapi import FastAPI, Depends, Request
from auth_middleware import JwtAuthMiddleware
from auth_middleware.guards import require_user, require_groups
from auth_middleware.providers.oidc.oidc_provider import OidcProvider
from auth_middleware.providers.oidc.oidc_provider_settings import OidcProviderSettings

app = FastAPI()

auth_settings = OidcProviderSettings(
    issuer="https://cognito-idp.us-east-1.amazonaws.com/us-east-1_abcdef123",
    audience="your-app-client-id",  # recommended: rejects tokens issued for other clients
    jwt_token_verification_disabled=False,
)

app.add_middleware(
    JwtAuthMiddleware,
    auth_provider=OidcProvider(settings=auth_settings),
)

# Requires valid authentication
@app.get("/protected", dependencies=[Depends(require_user())])
async def protected_endpoint(request: Request):
    user = request.state.current_user
    return {"message": f"Hello {user.name}", "user_id": user.id}

# Requires group membership
@app.get("/admin", dependencies=[Depends(require_groups(["admin", "moderator"]))])
async def admin_endpoint(request: Request):
    return {"message": "Admin access granted"}

Azure Entra ID

Set the required environment variables (read at module load time):

AUTH_PROVIDER_AZURE_ENTRA_ID_TENANT_ID=your-tenant-id
AUTH_PROVIDER_AZURE_ENTRA_ID_AUDIENCE_ID=your-app-client-id
from auth_middleware import JwtAuthMiddleware
from auth_middleware.providers.azure.entra_id_provider import EntraIDProvider

app.add_middleware(
    JwtAuthMiddleware,
    auth_provider=EntraIDProvider(),
)

Generic OIDC (Authentik, Keycloak, Auth0, Okta, ...)

Works with any standards-compliant OpenID Connect identity provider — the JWKS is discovered automatically from the issuer's .well-known/openid-configuration document.

from auth_middleware import JwtAuthMiddleware
from auth_middleware.providers.oidc.oidc_provider import OidcProvider
from auth_middleware.providers.oidc.oidc_provider_settings import OidcProviderSettings

auth_settings = OidcProviderSettings(
    issuer="https://authentik.example.com/application/o/my-app/",
    audience="your-oidc-client-id",  # recommended: rejects tokens from other clients
)

app.add_middleware(
    JwtAuthMiddleware,
    auth_provider=OidcProvider(settings=auth_settings),
)

Basic Auth

from auth_middleware import BasicAuthMiddleware, hash_password
from auth_middleware.contracts import CredentialsRepository
from auth_middleware.types.user_credentials import UserCredentials

# When creating a user, hash their password once and store the result
# (e.g. hashed_password column) — never store the plaintext password.
stored_hash = hash_password("their-password")

class MyCredentialsRepository(CredentialsRepository):
    async def get_by_id(self, *, id: str) -> UserCredentials | None:
        # Look up the user and return their stored credentials, including
        # the hash produced by hash_password() above
        return UserCredentials(id=id, name="Jane Doe", hashed_password=stored_hash)

app.add_middleware(
    BasicAuthMiddleware,
    credentials_repository=MyCredentialsRepository(),
)

Configuration

Environment Variables

# Core middleware — always read from environment
AUTH_MIDDLEWARE_DISABLED=false
AUTH_MIDDLEWARE_LOG_LEVEL=INFO

# Azure Entra ID — always read from environment at module load (required when using EntraIDProvider)
AUTH_PROVIDER_AZURE_ENTRA_ID_TENANT_ID=your-tenant-id
AUTH_PROVIDER_AZURE_ENTRA_ID_AUDIENCE_ID=your-client-id

# Generic OIDC (e.g. AWS Cognito) — optional; alternative to passing values programmatically to OidcProviderSettings
# OIDC_ISSUER=https://cognito-idp.us-east-1.amazonaws.com/us-east-1_abcdef123
# OIDC_AUDIENCE=your-app-client-id
# JWKS_CACHE_INTERVAL=20      # minutes, default 20
# JWKS_CACHE_USAGES=1000      # verifications before refresh, default 1000

SQL-backed Groups and Permissions

from auth_middleware.providers.oidc.oidc_provider import OidcProvider
from auth_middleware.providers.sqlalchemy.sql_groups_provider import SqlGroupsProvider
from auth_middleware.providers.sqlalchemy.sql_permissions_provider import SqlPermissionsProvider
from auth_middleware.providers.sqlalchemy.async_database import AsyncDatabase
from auth_middleware.providers.sqlalchemy.async_database_settings import AsyncDatabaseSettings

AsyncDatabase.configure(AsyncDatabaseSettings(
    database_url="postgresql+asyncpg://user:pass@localhost/mydb"
))

auth_provider = OidcProvider(
    settings=auth_settings,
    groups_provider=SqlGroupsProvider(),
    permissions_provider=SqlPermissionsProvider(),
)

app.add_middleware(JwtAuthMiddleware, auth_provider=auth_provider)

Cognito Groups Provider

from auth_middleware.providers.oidc.oidc_provider import OidcProvider
from auth_middleware.providers.aws.cognito_groups_provider import CognitoGroupsProvider

# Groups extracted directly from the cognito:groups JWT claim
auth_provider = OidcProvider(
    settings=auth_settings,
    groups_provider=CognitoGroupsProvider(),
)

Usage Examples

Accessing User Information

@app.get("/profile", dependencies=[Depends(require_user())])
async def get_profile(request: Request):
    user = request.state.current_user
    return {
        "id": user.id,
        "name": user.name,
        "email": user.email,
        "groups": await user.groups,
        "permissions": await user.permissions,
    }

Role and Permission Guards

from auth_middleware.guards import require_roles, require_permissions

@app.get("/reports", dependencies=[Depends(require_roles(["analyst", "manager"]))])
async def get_reports(request: Request):
    return {"message": "Reports access granted"}

@app.post("/admin/users", dependencies=[Depends(require_permissions(["user.create"]))])
async def create_user(request: Request):
    return {"message": "User creation allowed"}

curl

curl -H "Authorization: Bearer YOUR_JWT_TOKEN" \
     http://localhost:8000/protected

Package Structure

auth_middleware/
├── jwt_auth_middleware.py       # JwtAuthMiddleware (ASGI)
├── basic_auth_middleware.py     # BasicAuthMiddleware (ASGI)
├── constants.py                 # AUTH_SCHEME_BASIC, AUTH_SCHEME_BEARER
├── contracts/                   # Abstract base classes
│   ├── jwt_provider.py          #   JWTProvider
│   ├── groups_provider.py       #   GroupsProvider
│   ├── roles_provider.py        #   RolesProvider
│   ├── permissions_provider.py  #   PermissionsProvider
│   ├── profile_provider.py      #   ProfileProvider
│   └── credentials_repository.py #  CredentialsRepository
├── guards/                      # FastAPI dependency guards
│   ├── functions.py             #   require_user, require_groups, require_roles,
│   │                            #   require_permissions, get_current_user
│   ├── group_checker.py         #   GroupChecker
│   ├── role_checker.py          #   RoleChecker
│   └── permissions_checker.py   #   PermissionsChecker
├── providers/
│   ├── aws/                     # AWS Cognito groups/roles + auth service (pair with OidcProvider)
│   │   ├── cognito_groups_provider.py
│   │   ├── cognito_groups_as_roles_provider.py
│   │   ├── cognito_profile_provider.py
│   │   └── services/            #   Cognito auth service, M2M detector
│   ├── azure/                   # Azure Entra ID
│   │   ├── entra_id_provider.py
│   │   └── settings.py
│   ├── oidc/                    # Generic OIDC (AWS Cognito, Authentik, Keycloak, Auth0, ...)
│   │   ├── oidc_provider.py
│   │   └── oidc_provider_settings.py
│   └── sqlalchemy/              # SQL-backed authorization
│       ├── sql_groups_provider.py
│       ├── sql_permissions_provider.py
│       ├── async_database.py
│       └── async_database_settings.py
└── exceptions/                  # InvalidTokenException, AuthenticationError, …

Architecture

graph TD
    A[HTTP Request] --> B[JwtAuthMiddleware\nor BasicAuthMiddleware]
    B --> C[JWTBearerManager]
    C --> D[JWTProvider\nOidcProvider · EntraIdProvider]
    D --> E[Token Validation\nJWKS cache + exp/iss/aud]
    E --> F[User object]
    F --> G[GroupsProvider\nCognitoGroupsProvider · SqlGroupsProvider]
    F --> H[PermissionsProvider\nSqlPermissionsProvider]
    F --> I[RolesProvider\nCognitoGroupsAsRolesProvider]
    G --> J[request.state.current_user]
    H --> J
    I --> J
    J --> K[Guards\nrequire_user · require_groups\nrequire_roles · require_permissions]

Core Components

Component Import path Description
JwtAuthMiddleware auth_middleware ASGI middleware for JWT authentication
BasicAuthMiddleware auth_middleware ASGI middleware for Basic Auth
JWTProvider auth_middleware.contracts Abstract base for JWT providers
GroupsProvider auth_middleware.contracts Abstract base for group authorization
RolesProvider auth_middleware.contracts Abstract base for role authorization
PermissionsProvider auth_middleware.contracts Abstract base for permission authorization
CredentialsRepository auth_middleware.contracts Abstract base for Basic Auth credential lookup
require_user auth_middleware.guards Guard: requires authenticated user
require_groups auth_middleware.guards Guard: requires group membership
require_roles auth_middleware.guards Guard: requires role membership
require_permissions auth_middleware.guards Guard: requires specific permissions
get_current_user auth_middleware.guards Dependency: returns current user or None
CognitoGroupsProvider auth_middleware.providers.aws.cognito_groups_provider Groups from cognito:groups claim
CognitoGroupsAsRolesProvider auth_middleware.providers.aws.cognito_groups_as_roles_provider Cognito groups mapped as roles
EntraIDProvider auth_middleware.providers.azure.entra_id_provider Azure Entra ID JWT provider
OidcProvider auth_middleware.providers.oidc.oidc_provider Generic OIDC JWT provider (AWS Cognito, Authentik, Keycloak, Auth0, ...)
SqlGroupsProvider auth_middleware.providers.sqlalchemy.sql_groups_provider DB-backed groups
SqlPermissionsProvider auth_middleware.providers.sqlalchemy.sql_permissions_provider DB-backed permissions

Authenticated User Properties

Available on request.state.current_user:

Property Type Description
id str Unique identifier from the identity provider
name str | None Display name
email EmailStr | None Email address
groups list[str] (async) User groups
roles list[str] (async) User roles
permissions list[str] (async) User permissions

Development

# Clone and set up
git clone https://github.com/impalah/auth-middleware.git
cd auth-middleware
make venv

# Quality checks
make test          # run test suite
make type-check    # mypy strict
make lint          # ruff
make check         # all checks at once

# Build & publish
make build
make publish

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'feat: add amazing feature')
  4. Push the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

MIT — see LICENSE for details.

Links


Created by impalah — Made for modern Python async applications

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

auth_middleware-0.6.1.tar.gz (9.0 MB view details)

Uploaded Source

Built Distribution

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

auth_middleware-0.6.1-py3-none-any.whl (70.3 kB view details)

Uploaded Python 3

File details

Details for the file auth_middleware-0.6.1.tar.gz.

File metadata

  • Download URL: auth_middleware-0.6.1.tar.gz
  • Upload date:
  • Size: 9.0 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for auth_middleware-0.6.1.tar.gz
Algorithm Hash digest
SHA256 21676deb8fe5054e16229a32b0f96456c51ca76a4e590cb20f16ed0007c325f1
MD5 e78d7d43cd28c3d60e10ff57b8e3c1b9
BLAKE2b-256 46a33d901644f157bd808026025848438d1723b32d94330ed4ddc22d222987c5

See more details on using hashes here.

File details

Details for the file auth_middleware-0.6.1-py3-none-any.whl.

File metadata

  • Download URL: auth_middleware-0.6.1-py3-none-any.whl
  • Upload date:
  • Size: 70.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.12.3 {"installer":{"name":"uv","version":"0.12.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for auth_middleware-0.6.1-py3-none-any.whl
Algorithm Hash digest
SHA256 e809a2041d803d61aad8188ded9540088e661a90102634fb2294b0dc7611e240
MD5 6a5d94480528def8ddd148a4dd60a36d
BLAKE2b-256 6c13792d87b07bbe8f69f1fc0af71c6cc9758f218746a645ddab1c9cec53440e

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 Sentry Error logging StatusPage Status page