pydantic-jwt
JWT tokens declared and validated as typed Pydantic models — signing, parsing and claim checks come for free from the model definition.
What it is#
A library for handling JWTs the way you'd want to in a typed codebase: declare your token as a Pydantic model, and get parsing, claim validation, signature verification and encoding out of it for free. Claims are typed, autocompleted and checked like any other Pydantic field — no more untyped dicts of claims floating around an auth layer.
Why I built it#
Most JWT handling in Python means juggling raw dicts, manual jwt.encode /
jwt.decode calls, and ad-hoc validation of expiry, issuer and audience
scattered across the codebase. None of that fits naturally with a Pydantic-based
API like FastAPI, where everything else is a typed model. The idea was to make
the token itself the model — one class that both issues and reads tokens, with
claim rules (Exp, Nbf, Iat, IssClaim, AudClaim, custom claims)
expressed as annotated types instead of hand-rolled checks, and with a
verified_only mode that makes it structurally impossible to accept an
unverified payload where a real token was required.
How it works#
pip install pydantic-jwt
from pydantic_jwt import ConfigDict, Exp, JWTModel, after, uuid
SECRET = "keep-me-out-of-your-source"
class AccessToken(JWTModel):
model_config = ConfigDict(
algorithm="HS256",
encoding_key=SECRET,
decoding_key=SECRET,
)
sub: str
exp: Exp = after(minutes=15)
jti: str = uuid()
token = AccessToken(sub="user-42")
raw = token.generate() # issue
token = AccessToken.from_token(raw) # read + verify
Under the hood, JWTModel sits on top of Pydantic v2 and PyJWT: signing/keys
and algorithm come from model_config, claim markers (Exp, Nbf, IssClaim,
AudClaim, ...) are Annotated types that validate themselves against the
current time or expected values, and any failure surfaces as a normal
ValidationError with typed error codes (jwt_invalid_signature,
jwt_claim_invalid, jwt_format, jwt_unverified_payload, etc.) so it slots
into whatever error handling a Pydantic-based app already has. It integrates
cleanly with FastAPI via Depends, reporting itself to OpenAPI as a JWT-typed
string.
Status#
Published on PyPI, MIT-licensed, with CI (tests, ruff, mypy) and a MkDocs
documentation site (built as a Cloudflare Worker) covering models,
configuration, claims, defaults, validation, FastAPI integration, and security
notes. Core functionality — issuing, verified reading, claim markers, key
rotation, verified_only — is stable and documented; ongoing work is mostly
docs polish and expanding the custom-claims guide.