Repository navigation
Releases: dmi03/pydantic-jwt
Release list
v1.0.0
First stable release. The API is settled, and the sharp edge from 0.2.0 — a
model built from a dict looking exactly like a verified one — now has a way to
turn it off.
Full documentation: [pydantic-jwt.dmi03.com](https://pydantic-jwt.dmi03.com)
Added
verified_only— a model withmodel_config = ConfigDict(verified_only=True)
accepts nothing but a token string whose signature was checked. A claims dict
arriving from a request body is rejected withjwt_unverified_payload, so a
JWTModelcan safely be a field type. Off by default; models that read
incoming tokens should set it.from_claims()— build a model from claim values on averified_only
model, where the plain constructor is refused. Claims are still validated, and
a validation context can be passed as the first argument.
Changed
str(token)no longer signs. A model now stringifies like any other
Pydantic model, showing its claims. Signing happens only where you ask for it:
token.generate()ortoken.jwt_str.
Breaking changes
str(token) and f"{token}" used to return a signed, replayable token. They
now return the claims. Nothing raises — the value is simply no longer a token —
so grep for it:
# before
headers = {"Authorization": f"Bearer {token}"}
response = TokenPair(access_token=str(token))
# after
headers = {"Authorization": f"Bearer {token.jwt_str}"}
response = TokenPair(access_token=token.generate())The reason for the change: logger.info("token=%s", token) used to write a live
credential into the logs, where anyone who can read them can replay it until it
expires.
Models that opt into verified_only=True also lose the plain constructor —
use from_claims() there. Existing models are unaffected.
Requirements
Python 3.10+, Pydantic 2.10+, PyJWT.
pip install pydantic-jwtv0.2.0
The library grew from a JWT string type into a way to declare tokens as models.
0.1.1 shipped JWTStr and JWTConstraints; everything else here is new.
Full documentation: [pydantic-jwt.dmi03.com](https://pydantic-jwt.dmi03.com)
from pydantic_jwt import ConfigDict, Exp, JWTModel, after, uuid
class AccessToken(JWTModel):
model_config = ConfigDict(
algorithm="HS256",
encoding_key=SECRET,
decoding_key=SECRET,
)
sub: str
exp: Exp = after(minutes=15)
jti: str = uuid()
raw = str(AccessToken(sub="user-42")) # issue
token = AccessToken.from_token(raw) # read back, verifiedAdded
JWTModel— a Pydantic model that is also a JWT. One class issues tokens
(generate(),str(),.jwt_str) and validates incoming ones
(from_token(), or by validating a token string into the field), with the
signature verified through PyJWT. The algorithm comes from your configuration
and never from the token header, so a forgedalgcannot influence
verification.- Claim markers —
Exp,NbfandIatvalidate against the current clock
(with an optionalleewayfor clock skew);IssClaimandAudClaimvalidate
against an expected issuer and audience. All are plainAnnotatedmetadata,
so they compose with anything else Pydantic can do to a field, andClaimcan
be subclassed for your own. - Field defaults —
after(),at()anduuid()forexp,nbfand
jti, evaluated per instance so every token gets fresh values. ConfigDict— Pydantic's config extended withalgorithm,
encoding_key,decoding_keyandrequire_keys.- Per-call keys —
from_token()acceptsdecoding_key,algorithmand
require_keysoverriding the config, and reads the same values from the
validation context, for keys that are only known at request time. - OpenAPI support — a token model reports itself as
{"type": "string", "format": "jwt"}with its claims listed in the
description. - Documentation site — guides, a full API reference,
[security notes](https://pydantic-jwt.dmi03.com/guide/security/) and a
complete [FastAPI example](https://pydantic-jwt.dmi03.com/integrations/fastapi/)
with bearer authentication, refresh tokens and scopes.
Removed
JWTConstraints. Constraints are now expressed as claim markers on a
JWTModelfield, which validate the parsed claim instead of the raw string.
JWTStris unchanged and still does structural validation only.
Fixed
- The validation context is now forwarded through
from_token(), so
context={"validate_claims": False}works for tokens validated from a token
string and not only from a dict.
Requirements
Python 3.10+, Pydantic 2.10+, PyJWT.
pip install pydantic-jwtv0.1.1
v0.1.1
Initial public release of pydantic-jwt.
What it does
JWTStr — a string subclass that validates a value is a structurally
well-formed JSON Web Token (RFC 7519): three base64url-encoded segments,
valid JSON header and payload, and a non-empty alg. It does not
verify the cryptographic signature — pair it with a library like PyJWT
for that.
from pydantic import BaseModel
from pydantic_jwt import JWTStr
class Auth(BaseModel):
token: JWTStr
auth = Auth(token="eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dGVzdA")
auth.token.header # {'alg': 'HS256'}
auth.token.payload # {'sub': '1234567890'}
auth.token.algorithm # 'HS256'Features
JWTStrPydantic type withheader,payload,algorithm, and
signaturepropertiesJWTConstraints— opt-in extra checks viaAnnotated:- restrict allowed algorithms
- reject expired tokens (
exp) and not-yet-valid tokens (nbf) - support for custom claim names
- Full JSON Schema support (
format: jwt) - Tested on Python 3.10–3.14
Install
pip install pydantic-jwt