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

  • AWS Cognito — Full integration with Amazon Cognito User Pools
  • AWS Cognito Identity Pool — Exchange User Pool tokens for temporary AWS credentials
  • Azure Entra ID — Microsoft Azure Active Directory authentication
  • Generic OIDC — Any standards-compliant OpenID Connect provider (Authentik, Keycloak, Auth0, Okta, ...) via OidcProvider
  • 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

from fastapi import FastAPI, Depends, Request
from auth_middleware import JwtAuthMiddleware
from auth_middleware.guards import require_user, require_groups
from auth_middleware.providers.aws.cognito_provider import CognitoProvider
from auth_middleware.providers.aws.cognito_authz_provider_settings import CognitoAuthzProviderSettings

app = FastAPI()

auth_settings = CognitoAuthzProviderSettings(
    user_pool_id="us-east-1_abcdef123",
    user_pool_region="us-east-1",
    user_pool_client_id="your-app-client-id",  # recommended: rejects tokens from other app clients
    jwt_token_verification_disabled=False,
)

app.add_middleware(
    JwtAuthMiddleware,
    auth_provider=CognitoProvider(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

# AWS Cognito — optional; alternative to passing values programmatically to CognitoAuthzProviderSettings
# USER_POOL_ID=us-east-1_abcdef123
# USER_POOL_REGION=us-east-1
# 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.aws.cognito_provider import CognitoProvider
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 = CognitoProvider(
    settings=auth_settings,
    groups_provider=SqlGroupsProvider(),
    permissions_provider=SqlPermissionsProvider(),
)

app.add_middleware(JwtAuthMiddleware, auth_provider=auth_provider)

Cognito Groups Provider

from auth_middleware.providers.aws.cognito_provider import CognitoProvider
from auth_middleware.providers.aws.cognito_groups_provider import CognitoGroupsProvider

# Groups extracted directly from the cognito:groups JWT claim
auth_provider = CognitoProvider(
    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 & Identity Pool
│   │   ├── cognito_provider.py
│   │   ├── cognito_groups_provider.py
│   │   ├── cognito_groups_as_roles_provider.py
│   │   ├── cognito_profile_provider.py
│   │   ├── cognito_authz_provider_settings.py
│   │   ├── identity_pool_provider.py
│   │   └── services/            #   Cognito auth service, M2M detector
│   ├── azure/                   # Azure Entra ID
│   │   ├── entra_id_provider.py
│   │   └── settings.py
│   ├── oidc/                    # Generic OIDC (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\nCognitoProvider · EntraIdProvider · IdentityPoolProvider · OidcProvider]
    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
CognitoProvider auth_middleware.providers.aws.cognito_provider AWS Cognito JWT provider
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
IdentityPoolProvider auth_middleware.providers.aws.identity_pool_provider Cognito + Identity Pool
EntraIDProvider auth_middleware.providers.azure.entra_id_provider Azure Entra ID JWT provider
OidcProvider auth_middleware.providers.oidc.oidc_provider Generic OIDC JWT provider (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.5.0.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.5.0-py3-none-any.whl (77.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: auth_middleware-0.5.0.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.5.0.tar.gz
Algorithm Hash digest
SHA256 2982276f7784d1b75e198432b7c1f126de1310b4fa8f049d9b0415001719d460
MD5 0045cd37b6c19b71e8722a12c484abb4
BLAKE2b-256 09922b35ab0b5b205bced907df46e8218acbc5fc3107ada636af5cebdb729e51

See more details on using hashes here.

File details

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

File metadata

  • Download URL: auth_middleware-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 77.5 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.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 4a704bcf013cd699c318b36b63ad379270f7dcacba10e78985f3103f735f50cb
MD5 4ee7e60c574f76ee5e0da100bea725ca
BLAKE2b-256 b9bc4adc12b56ed2197094461679220ed082ed0a8d285e6dbdaf8ed4d1916f31

See more details on using hashes here.

Release history Release notifications | RSS feed

0.6.1

2 files

0.6.0

2 files

This release

0.5.0 This release

2 files

0.4.6

2 files

0.4.5

2 files

0.4.4

2 files

0.4.3

2 files

0.4.2

2 files

0.3.9

2 files

0.3.8

2 files

0.3.7

2 files

0.3.5

2 files

0.3.4

2 files

0.3.3

2 files

0.3.2

2 files

0.2.17

2 files

0.2.16

2 files

0.2.15

2 files

0.2.14

2 files

0.2.13

2 files

0.2.12

2 files

0.2.9

2 files

0.2.8

2 files

0.2.7

2 files

0.2.6

2 files

0.2.5

2 files

0.2.4

2 files

0.2.3

2 files

0.2.2

2 files

0.2.1

2 files

0.1.23

2 files

0.1.22

2 files

0.1.20

2 files

0.1.19

2 files

0.1.18

2 files

0.1.17

2 files

0.1.16

2 files

0.1.14

2 files

0.1.13

2 files

0.1.12

2 files

0.1.11

2 files

0.1.10

2 files

0.1.9

2 files

0.1.8

2 files

0.1.7

2 files

0.1.6

2 files

0.1.4

2 files

0.1.3

2 files

0.1.2

2 files

0.1.1

2 files

Supported by

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