2.7 KiB
MWT — Mizan Web Token
Decided 2026-04-06.
MWT is a standard JWT (RFC 7519, HMAC-SHA256) with Mizan-specific
claims, traveling on the X-Mizan-Token header. It is the
protocol's identity layer for cache keying and permission staleness
detection.
Claims
| Claim | Purpose |
|---|---|
sub |
User ID — goes into HMAC cache key derivation |
pkey |
Deterministic hash of user's permission state at issuance |
exp |
Configurable short TTL — controls permission staleness window (Django setting) |
iat |
Issued at |
kid |
Key ID — for secret rotation. Carried in the JOSE header (RFC 7515), not the payload |
aud |
Audience binding — prevents cross-tenant replay |
nbf |
Not-before — tolerates clock skew |
staff / super |
is_staff / is_superuser, used to build MWTUser without a DB query |
Key decisions
- Standard JWT envelope, not proprietary. Uses standard libraries for signing and validation.
X-Mizan-Tokenheader, notAuthorization: Bearer. Avoids collision with DRF, allauth, and existing JWT systems. Cloudflare WAF/Access do not inspect custom headers.MWTUseris a minimal, DB-free request user built from the token claims (cores/mizan-python/src/mizan_core/mwt.py).A separate JWT module (
mizan/jwt/) still exists for standard user-auth access/refresh tokens; MWT is the cache-keying identity layer, not a replacement for that module.- App handles authentication (session, social, etc.). Mizan
issues MWT from the authenticated identity
(
create_mwt(user, secret, ttl, audience, kid)). - Edge Worker validates MWT, extracts
subfor HMAC cache key, checksexp. pkeycomputation must be deterministic:sorted(user.get_all_permissions())then hash.- Client-side: proactive refresh before expiry. Check TTL before dispatch, not reactively after a 401.
- Header-based, not cookie-based. A cookie would force
Vary: Cookie, destroying PSR cache.
HMAC canonical form
JSON with sorted keys:
ctx:{context}:HMAC(secret, JSON.stringify({"c": context, "p": sorted_params, "r": rev, "u": user_id}))
See CACHE_KEYING.md for the full derivation.
What this solves
- DRF token collision
JWTUser-too-thin problem- Permission staleness race condition
- Single validation path across Python and TypeScript Edge
Usage rule
MWT is the identity Edge/cache layers key on. The @client(auth=...)
parameter is enforced server-side in mizan/client/executor.py
(_check_auth_requirement), which checks request.user against the
auth requirement (required / staff / superuser / callable);
request.user may be an MWTUser (stateless) or a session user.