Authentication middleware — dual-mode session + token auth.
Authenticates requests via session cookies (browsers) or bearer tokens
(API clients). The authenticated user is stored in a ContextVar,
accessible viaget_user()from any handler or middleware.
RequiresSessionMiddlewarefor session-based auth. Token auth
works independently.
Usage::
from chirp.middleware.auth import AuthConfig, AuthMiddleware, get_user, login, logout
from chirp.middleware.sessions import SessionConfig, SessionMiddleware
app.add_middleware(SessionMiddleware(SessionConfig(secret_key="...")))
app.add_middleware(AuthMiddleware(AuthConfig(
load_user=my_load_user, # async (id: str) -> User | None
verify_token=my_verify_token, # async (token: str) -> User | None
)))
# In a handler:
user = get_user()
if user.is_authenticated:
...
# Login/logout:
await login(user)
await logout()
middleware.auth
| Name | Type | Default | Description |
|---|---|---|---|
type
|
|
— | |
qualified_name
|
|
— | |
element_type
|
|
— | |
description
|
|
— | |
source_file
|
|
— | |
line_number
|
|
— | |
is_autodoc
|
|
— | |
autodoc_element
|
|
— | |
_autodoc_template
|
|
— | |
_autodoc_url_path
|
|
— | |
_autodoc_page_type
|
|
— | |
title
|
|
— | |
doc_content_hash
|
|
— |
Symbols on this page
Minimal user protocol.
Any object withid and is_authenticatedsatisfies this.
Developers bring their own user model — ORM class, dataclass, etc.
Extended user protocol with permission support.
Used by@requires(*permissions)to check access.
Machine-client protocol with token-scope support.
The machine-auth counterpart toUserWithPermissions: a
verify_token-resolved client (webhook / cron / provisioning caller) exposes the token's…
Protocol for an app-supplied bearer-token revocation backend.
The stateless bearer path (verify_token) has no built-in revocation: once a token verifies, it stays…
Sentinel for unauthenticated requests.
Returned byget_user()when no user is authenticated.
Eliminates null checks —get_user() never returns None.
Re-establish the auth user while a streaming generator drains.
Return the current authenticated user (orAnonymousUser).
RaisesLookupErrorif called outside a request with
AuthMiddlewareactive.
Inside anEventStreamgenerator (SSE), this…
Log in a user — regenerate session, set user ID, update ContextVar.
Regenerates the session to prevent session fixation attacks. Call from your login…
Log out the current user — regenerate session + clear ContextVar.
Regenerates the session to discard all session data (not just the user ID).…
Authentication middleware configuration.
Return the current user for templates.
Template-friendly alias forget_user(). Returns AnonymousUser
if no user is authenticated, never raises.
Registered as a template…
Dual-mode authentication middleware.
Tries token auth first (stateless, for API clients), then falls back to session auth (stateful, for browsers). Sets the authenticated user…
Alias ofmiddleware.auth.ClientWithScopes
User
class
Minimal user protocol.
Any object withid and is_authenticatedsatisfies this.
Developers bring their own user model — ORM class, dataclass, etc.
UserWithPermissions
class
Extended user protocol with permission support.
Used by@requires(*permissions)to check access.
ClientWithScopes
class
Machine-client protocol with token-scope support.
The machine-auth counterpart toUserWithPermissions: a
verify_token-resolved client (webhook / cron / provisioning caller)
exposes the token's scopes so a declarativeAuthSpec(scopes=...)can gate
on them independently of human permissions. The scope axis is deliberately
separate frompermissions— a machine client need not implement
UserWithPermissions, and a human user need not implement this
protocol; the shared gate checks each axis only when the activeAuthSpec
declares it.
scopes is a frozenset[str] (same shape as permissions). Bring
your own client model — any object withid, is_authenticated, and
scopessatisfies it. The scope-bearing client flows through
_authenticate_token() unchanged.
TokenRevocationStore
class
Protocol for an app-supplied bearer-token revocation backend.
The stateless bearer path (verify_token) has no built-in revocation:
once a token verifies, it stays valid until it expires. A revocation store
closes that gap — it is consulted afterverify_tokenreturns a user
(token branch only) and gives two revocation axes that mirror how
session_versiongives the session path mass revocation:
- per-token:
is_token_revoked() rejects a single revokedjti; - per-user cutoff:
user_revoked_at() rejects every token a user was issued before arevoked_attimestamp (tokeniat <= revoked_at).
Both axes require token claims, which Chirp does not decode itself — supply
token_claims to surface {jti, sub, iat}from the
opaque token. Withtoken_claims unset, only is_token_revoked() can
run (and only if the store is reachable without ajti); the cutoff axis
is skipped.
The store is app-supplied and async. Chirp holds no lock — the store is
responsible for its own concurrency (mirrorsSessionStore). On a
store error Chirp fails open (treats the token as not revoked) and emits
anauth.token.revocation_check_errorsecurity event, so a backend blip
does not 401 every API client.
AnonymousUser
class
Sentinel for unauthenticated requests.
Returned byget_user()when no user is authenticated.
Eliminates null checks —get_user() never returns None.
_set_stream_user
function
def _set_stream_user(user: User) -> Token[User]
Re-establish the auth user while a streaming generator drains.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
user
|
User
|
— |
get_user
function
def get_user() -> User
Return the current authenticated user (orAnonymousUser).
RaisesLookupErrorif called outside a request with
AuthMiddlewareactive.
Inside anEventStreamgenerator (SSE), this returns the user captured
at connect time. SSE identity is pinned for the connection's lifetime:
a user logged out or permission-revoked mid-stream keeps the connect-time
identity until they reconnect. Callkick_user() to
terminate that user's live streams so htmx reconnect re-runs auth middleware
and re-pins fresh permissions. An unauthenticated connection sees
AnonymousUserfor the whole stream.
No parameters.
login
function
def login(user: User) -> None
Log in a user — regenerate session, set user ID, update ContextVar.
Regenerates the session to prevent session fixation attacks. Call from your login handler after verifying credentials::
user = await verify_credentials(email, password)
if user:
login(user)
return Redirect("/dashboard")
RequiresSessionMiddleware and AuthMiddlewareto be active.
Parameters
| Name | Type | Default | Description |
|---|---|---|---|
user
|
User
|
— |
logout
function
def logout() -> None
Log out the current user — regenerate session + clear ContextVar.
Regenerates the session to discard all session data (not just the user ID). Call from your logout handler::
logout()
return Redirect("/")
RequiresSessionMiddleware and AuthMiddlewareto be active.
No parameters.
AuthConfig
class
Authentication middleware configuration.
current_user
function
def current_user() -> User
Return the current user for templates.
Template-friendly alias forget_user(). Returns AnonymousUser
if no user is authenticated, never raises.
Registered as a template global whenAuthMiddlewareis active::
{% if current_user().is_authenticated %}
<a href="/profile">{{ current_user().name }}</a>
{% else %}
<a href="/login">Sign in</a>
{% endif %}
Inside anEventStreamgenerator (SSE), this returns the user captured at
connect time — seeget_user() for the pinned-identity semantics.
No parameters.
AuthMiddleware
class
Dual-mode authentication middleware.
Tries token auth first (stateless, for API clients), then falls
back to session auth (stateful, for browsers). Sets the authenticated
user in a ContextVar accessible viaget_user().
Middleware ordering::
app.add_middleware(SessionMiddleware(...)) # 1st: sessions
app.add_middleware(AuthMiddleware(...)) # 2nd: auth
app.add_middleware(CSRFMiddleware()) # 3rd: CSRF
Usage::
from chirp.middleware.auth import AuthConfig, AuthMiddleware
app.add_middleware(AuthMiddleware(AuthConfig(
load_user=db.get_user_by_id,
verify_token=db.get_user_by_token,
)))
MachineClient
alias
Alias ofmiddleware.auth.ClientWithScopes
View source · /home/runner/work/chirp/chirp/site/../src/chirp/middleware/auth.py:1