fastapi-auth-manager-dep
Reusable authentication dependency for FastAPI with API Key and JWT Bearer support.
- One mandatory ADMIN key via envvar (super-key, always valid)
- Additional api-keys with labels/roles via JSON envvar
- Fine-grained per-endpoint control: which roles each endpoint accepts
- HMAC JWT with multiple keys (
kid), per-key algorithms, audience, issuer and required claims - Public endpoints via explicit opt-out
Install
pip install fastapi-auth-manager-dep # api-keys only
pip install "fastapi-auth-manager-dep[jwt]" # + JWT Bearer (installs pyjwt)
pip install "fastapi-auth-manager-dep[all]" # everything
JWT support needs the jwt extra: enabling "jwt" without pyjwt installed raises
ImportError when the AuthDependency is created.
Environment variables
| Variable | Required | Description |
|---|---|---|
AUTH_ADMIN_API_KEY |
Yes | Administrator api-key. Super-key: valid on every endpoint. |
AUTH_API_KEYS |
No | JSON object mapping api-keys to their labels. See format below. |
AUTH_JWT_KEYS |
No* | JSON object mapping JWT key ids to their settings. See format below. Required when using "jwt". |
AUTH_JWT_SECRET_KEY |
No | Deprecated — single HMAC secret, same as AUTH_JWT_KEYS={"default": {...}} with no required claims. |
AUTH_JWT_ALGORITHMS |
No | Deprecated — algorithms for AUTH_JWT_SECRET_KEY (default: ["HS256", "HS384", "HS512"]). |
AUTH_API_KEYS format
A JSON object where each key is the raw api-key value and the value is its label/role:
AUTH_API_KEYS='{"key-abc123": "reports", "key-xyz789": "billing", "key-qrs456": "billing"}'
- Multiple keys can share the same label (same role).
- Empty string (
AUTH_API_KEYS="") is equivalent to having no additional keys.
AUTH_JWT_KEYS format
A JSON object where each key is a key id and the value its settings:
AUTH_JWT_KEYS='{
"mobile": {"secret": "mobile-secret", "algorithms": ["HS256"]},
"partner": {"secret": "partner-secret", "algorithms": ["HS512"], "audience": "billing-api",
"issuer": "partner-sso", "leeway": 30, "require": ["exp", "sub"]}
}'
| Field | Required | Default | Description |
|---|---|---|---|
secret |
Yes | — | HMAC secret (non-empty). |
algorithms |
No | ["HS256", "HS384", "HS512"] |
Allowed algorithms; HMAC only. |
audience |
No | not checked | Expected aud claim (string or list). |
issuer |
No | not checked | Expected iss claim. |
leeway |
No | 0 |
Clock-skew tolerance in seconds for exp/nbf/iat. |
require |
No | ["exp"] |
Claims that must be present; [] disables it. |
- Tokens select their key with the
kidheader. Tokens withoutkidare verified with the key whose id is"default", if there is one; otherwise they are rejected. - Key ids cannot be empty or contain
:. - Migrating from
AUTH_JWT_SECRET_KEY: move the secret toAUTH_JWT_KEYS='{"default": {"secret": "...", "require": []}}'— tokens withoutkidkeep working. Setting bothAUTH_JWT_SECRET_KEYand a"default"key is an error.
.env example
AUTH_ADMIN_API_KEY=super-secret-admin-key
AUTH_API_KEYS={"key-reports-1": "reports", "key-billing-1": "billing", "key-billing-2": "billing"}
AUTH_JWT_KEYS={"mobile": {"secret": "mobile-secret"}, "partner": {"secret": "partner-secret", "audience": "billing-api"}}
Usage
1. Global authentication (entire app)
All endpoints are protected with the ADMIN key by default:
from fastapi import Depends, FastAPI
from fastapi_auth import AuthDependency
app = FastAPI(dependencies=[Depends(AuthDependency())])
The most specific declaration wins: an AuthDependency or PublicRoute declared on a
router, on a route's dependencies or as a handler parameter replaces the global one for
that route, so it can widen access (e.g. accept "reports" keys) as well as narrow it.
Only top-level route dependencies count, not ones nested inside your own dependencies.
2. Restrict by api-key label
Only the ADMIN key and keys labelled "reports" can access:
from fastapi_auth import AuthDependency
@app.get(
"/reports",
dependencies=[Depends(AuthDependency(valid_token_types={"reports"}))]
)
async def get_reports():
...
3. Combine api-key and JWT on the same endpoint
from fastapi_auth import AuthDependency
@app.get(
"/billing",
dependencies=[Depends(AuthDependency(valid_token_types={"billing", "jwt"}))]
)
async def get_billing():
...
4. Allow all additional api-keys
from fastapi_auth import AuthDependency
# Using the AuthDependency.ALL sentinel
@app.get(
"/any",
dependencies=[Depends(AuthDependency(valid_token_types=AuthDependency.ALL))]
)
async def any_key_endpoint():
...
# Equivalent using a string
AuthDependency(valid_token_types="*")
5. Access the authenticated principal inside a handler
AuthDependency returns an AuthPrincipal with the method used, role, and JWT payload:
from fastapi_auth import AuthDependency, AuthPrincipal
@app.get("/me")
async def me(
principal: AuthPrincipal = Depends(
AuthDependency(valid_token_types={"jwt", "billing"})
)
):
return {
"method": principal.method, # "jwt" | "api_key"
"sub": principal.sub, # user_id (JWT) or raw key value (api-key)
"role": principal.role, # "admin" | "reports" | "billing" | None (JWT)
"payload": principal.payload, # full JWT dict | None
}
6. Public endpoint (opt-out of global auth)
When auth is configured globally, use PublicRoute to exclude specific endpoints
(requests are accepted with or without credentials):
from fastapi_auth import PublicRoute
@app.get("/health", dependencies=[Depends(PublicRoute())])
async def health():
return {"status": "ok"}
valid_token_types behaviour reference
valid_token_types |
ADMIN key | Additional keys | User JWT |
|---|---|---|---|
None (default) |
Yes | No | No |
{"reports"} |
Yes | reports label only |
No |
{"billing", "jwt"} |
Yes | billing label only |
Yes |
AuthDependency.ALL / "*" |
Yes | All | No |
{"jwt"} |
Yes | No | Any key |
{"jwt:partner"} |
Yes | No | partner key only |
The ADMIN key is always valid regardless of the endpoint configuration.
AuthPrincipal — return object
class AuthPrincipal(BaseModel):
method: AuthMethod # AuthMethod.JWT | AuthMethod.AUTH_ADMIN_API_KEY
sub: str # user_id (JWT) or raw api-key value
role: str | None = None # key role/label; None for JWT
payload: dict | None = None # full JWT payload; None for api-key
key_id: str | None = None # id of the JWT key that verified the token; None for api-key
Error responses
All authentication errors return HTTP 401 Unauthorized with a detail field:
| Situation | detail |
|---|---|
| No credentials provided | "Authentication credentials are required" |
| Api-key not found or not allowed | "Invalid authentication credentials" |
| Expired JWT | "JWT token has expired" |
| Invalid JWT signature | "Invalid JWT token: ..." |
| Disallowed JWT algorithm | "JWT algorithm not allowed: RS256" |
JWT kid unknown or not allowed on the endpoint |
"JWT key not allowed" |
JWT without kid and no "default" key |
"JWT key id (kid) is required" |
| Missing required claim, wrong audience/issuer | "Invalid JWT token: ..." |
| JWT key misconfigured on the server | "Invalid authentication credentials" |
Enabling
"jwt"without any JWT key configured, or"jwt:<id>"with an unknown id, raisesValueErrorwhen theAuthDependencyis created, so the misconfiguration fails at startup.
Tests
Development uses uv; test tools are in the dev dependency group:
uv run pytest -v
Test coverage includes:
- ADMIN key as super-key across endpoints with different restrictions
- Label-based restriction: accepts the correct label, rejects others
- Multiple keys sharing the same label
ALLsentinel and its"*"string equivalent- Valid JWT, expired JWT, JWT ignored when
"jwt"is not enabled - JWT enabled without a secret (fails at startup, never HTTP 500)
- No credentials
- Multiple JWT keys:
kidselection,"jwt:<id>"restriction,"default"key for tokens withoutkid, per-key algorithms, audience, issuer, leeway and required claims AUTH_JWT_KEYSvalidation and the deprecatedAUTH_JWT_SECRET_KEYalias- Installing without the
jwtextra - Global auth over HTTP:
PublicRouteopt-out, route-level widening/narrowing, principal from a handler parameter AUTH_API_KEYSvalidation in settings (JSON string, dict, invalid JSON)
Full example
from fastapi import Depends, FastAPI
from fastapi_auth import AuthDependency, AuthPrincipal, PublicRoute
app = FastAPI(dependencies=[Depends(AuthDependency())]) # global: ADMIN key only
@app.get("/health", dependencies=[Depends(PublicRoute())])
async def health():
return {"status": "ok"}
@app.get("/reports", dependencies=[Depends(AuthDependency(valid_token_types={"reports"}))])
async def reports():
return {"data": "..."}
@app.get("/me")
async def me(
principal: AuthPrincipal = Depends(AuthDependency(valid_token_types={"jwt"}))
):
return {"sub": principal.sub, "payload": principal.payload}
@app.get("/internal", dependencies=[Depends(AuthDependency(valid_token_types=AuthDependency.ALL))])
async def internal():
return {"message": "any valid api-key accepted"}
A runnable version lives in examples/example_app.py.
Release files for fastapi-auth-manager-dep 0.2.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fastapi_auth_manager_dep-0.2.1.tar.gz | 21.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fastapi_auth_manager_dep-0.2.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 36.9 kB
Release files / fastapi_auth_manager_dep-0.2.1.tar.gz
| Download URL | fastapi_auth_manager_dep-0.2.1.tar.gz |
|---|---|
| Size | 21.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
b7664e303c00b0b10df0fba1cd9c09f0e4fd81eb092e2d8dd333a8795fdb2b68
|
|
BLAKE2b-256 checksum How to use checksums |
232ccd8d2016af62482eee38b23299a955c853a061d6becd13ad468d929bd706
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","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}
|
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 Sep 24, 2026.
Transparency logRelease files / fastapi_auth_manager_dep-0.2.1-py3-none-any.whl
| Download URL | fastapi_auth_manager_dep-0.2.1-py3-none-any.whl |
|---|---|
| Size | 15.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
406b6989db5496a481547e699fbb73ada75450c33fc2066cfe5ac28023f6196a
|
|
BLAKE2b-256 checksum How to use checksums |
29f68658153c2e060a18fd3a216f81c580c11191b6d27748322396156ff54cc6
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.18 {"installer":{"name":"uv","version":"0.12.18","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}
|
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 Sep 24, 2026.
Transparency log