Skip to content

Releases: dmi03/pydantic-jwt

v1.0.0

Choose a tag to compare

@dmi03 dmi03 released this 30 Aug 09:47
4f6fb90

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 with model_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 with jwt_unverified_payload, so a
    JWTModel can 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 a verified_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() or token.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-jwt

v0.2.0

Choose a tag to compare

@dmi03 dmi03 released this 29 Aug 15:29
e37a651

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, verified

Added

  • 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 forged alg cannot influence
    verification.
  • Claim markers — Exp, Nbf and Iat validate against the current clock
    (with an optional leeway for clock skew); IssClaim and AudClaim validate
    against an expected issuer and audience. All are plain Annotated metadata,
    so they compose with anything else Pydantic can do to a field, and Claim can
    be subclassed for your own.
  • Field defaults — after(), at() and uuid() for exp, nbf and
    jti, evaluated per instance so every token gets fresh values.
  • ConfigDict — Pydantic's config extended with algorithm,
    encoding_key, decoding_key and require_keys.
  • Per-call keys — from_token() accepts decoding_key, algorithm and
    require_keys overriding 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
    JWTModel field, which validate the parsed claim instead of the raw string.
    JWTStr is 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-jwt

v0.1.1

Choose a tag to compare

@dmi03 dmi03 released this 24 Aug 15:20
100c844

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

  • JWTStr Pydantic type with header, payload, algorithm, and
    signature properties
  • JWTConstraints — opt-in extra checks via Annotated:
    • 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