This guide installs that source and wires it into FastAPI, Django, Flask, Starlette, or aiohttp, with one rule: require_auth on anything that must not be public, and a 401 when the token is missing.
Prerequisites
- Python 3.10 or newer.
- An Authdog environment and its publishable public key (
pk_...). - A commit or a reviewed snapshot of
packages/python. Pin it. Do not float onmainin production. - A client that can obtain a session for that same environment. A token from another environment will look like "every request is anonymous."
The package depends on httpx. Extras cover FastAPI 0.110, Django 4.2, Flask 3.0, Starlette 0.37, and aiohttp 3.9. There is no registry release, so there is no version solve and no provenance check from pip. The pin is the control.
Implementation
1. Build the client with the public key
Each framework module exports Authdog. Construct it with the environment's public key:
import os
from authdog.fastapi import Authdog
authdog = Authdog(public_key=os.environ["PK_AUTHDOG"])The core looks for authdog-session first, then Authorization: Bearer <token>. It calls that environment's OIDC userinfo endpoint and returns an AuthdogContext. If userinfo fails, you get an anonymous context. You do not get a half-authenticated user.
2. Use `session` to render, and `require_auth` to protect
session is for pages where anonymous and signed-in are both valid responses. It tells you which one you got:
@app.get("/")
async def index(ctx=Depends(authdog.session)):
return {"authenticated": ctx.is_authenticated}Hit / with no token. is_authenticated should be false, and the handler should still run.
require_auth is the gate. It rejects an unauthenticated request with 401:
@app.get("/me")
async def me(user=Depends(authdog.require_auth)):
return userHit /me with no token and expect 401. Hit it with a valid session and expect the user. A missing token, a bad token, or a failed userinfo call must not authenticate the request. If /me returns 200 for an empty header, the route is using session.
3. Call it the way that framework expects
Django takes authdog.session(request) and @authdog.require_auth. Flask takes authdog.session() and @authdog.require_auth. Starlette takes await authdog.session(request) and await authdog.require_auth(request). aiohttp takes await authdog.session(request) and @authdog.require_auth.
The logout helpers clear the local cookie and redirect the way that framework usually does. They do not revoke a bearer token, and they do not end the session at the upstream provider. After logout, call a protected route. You want 401 from the missing cookie. A bearer token you issued some other way can still be valid until it expires or you revoke it.
Security considerations
sessiondescribes the request.require_authrejects it. Every protected route uses the second one.- After
require_auth, check role, permission, or ownership. Missing authentication is401. Authenticated and not allowed is403. fetch_user=Falseis not a shortcut for authentication. It leaves you holding an unverified token withis_authenticatedfalse. Do not treat that token as a user.- Keep the public key in configuration. Hard-coding it is how a development key ends up in production.
- Do not log cookies or bearer tokens.
Validation checklist
- Python runtime is 3.10 or newer.
- Integration source is reviewed and pinned to a commit.
- No deployment step expects
authdog-fastapifrom PyPI. - Anonymous route returns
is_authenticated == Falsefor missing or invalid tokens. - Protected route returns
401without a validated user. - Authorization failure returns
403after authentication. - Logs redact cookies and bearer tokens.
Troubleshooting
pip cannot find authdog-fastapi. There is no PyPI release. Point the build at the pinned source snapshot. pip install authdog is a different package, the management API client, and it will not give you authdog.fastapi.
Every request is anonymous. The client is not sending authdog-session or a bearer token, or the token is for another environment, or userinfo is unreachable from this process. Fix the header and the network path before you change the dependency.
The route has a context and is still public. It depends on session. Switch that route to require_auth and repeat the unauthenticated request. You want 401.
Next steps
- Python authentication for the install and the session gate in more detail.
- Python access control after you confirm the claim names in your environment.
- Backend requests for the same validation model in other stacks.
- Watch the source repository for a real
authdog-fastapirelease before you switch this pin to a registry install.