A modern implementation of JSON Web Token (JWT) for Python.
With powerful Pydantic validation features.
Overview & Installation
SuperJWT is a minimalist JWT library for Python 3.10+ that combines the simplicity of JWT encoding/decoding with the power of Pydantic validation. It supports JWS (JSON Web Signature) format, HMAC and asymmetric algorithms (RSA, ECDSA, EdDSA). SuperJWT includes advanced features like enhanced time integrity checks, compact token inspection, custom timestamp serialization, detached payload mode, time spoofing and more.
Key Features:
- 🔐 Secure by default - JWS signature algorithm required.
- 🪶 Minimalist - Clean, modern code with minimal dependencies.
- ✔️ JWT validation - Easy claims validation with Pydantic models.
- 🏷️ Type hints - IDE autocompletion with your JWT claims or JOSE headers.
Install via pip:
pip install superjwt
Usage
SuperJWT makes it easy to encode and decode JWT tokens with automatic validation and serialization. Here are the fundamental operations:
Basic Usage 🐣
Encode manually your claims from a dict. During decoding, validate your JWT content against a standard JWT claims Pydantic model.
from superjwt import Alg, JWTClaims, encode, decode
secret_key = "your-secret-key-of-len-32-bytes!"
compact: bytes = encode({"iss": "my-app", "sub": "John Doe"}, secret_key, Alg.HS256)
print(compact)
#> b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
# .eyJpc3MiOiJteS1hcHAiLCJzdWIiOiJKb2huIERvZSJ9
# .HwnUqTLFAMzNkMrokd0aI7c-zSJJpSVXMrYIhUyWe4s'
decoded: JWTClaims = decode(compact, secret_key, Alg.HS256)
print(decoded.to_dict())
#> {'iss': 'my-app', 'sub': 'John Doe'}
print(decoded.sub)
#> 'John Doe'
Define dynamically your claims with Pydantic and easily include 'iat' (Issued At) and 'exp' (Expiration).
Validate your JWT content automatically during encoding and decoding.
from superjwt import Alg, JWTClaims, encode, decode
secret_key = "your-secret-key-of-len-32-bytes!"
claims = (
JWTClaims(iss="my-app", sub="John Doe")
.with_issued_at()
.with_expiration(minutes=15)
)
compact: bytes = encode(claims, secret_key, Alg.HS256)
decoded: JWTClaims = decode(compact, secret_key, Alg.HS256)
print(decoded.to_dict())
#> {'iss': 'my-app', 'sub': 'John Doe', 'iat': 1767027483, 'exp': 1767028383}
print(decoded.exp)
#> 2025-12-29 17:13:03
Custom Claims and Validation
Redefine standard claims or define new custom ones. Validate automatically during encoding and decoding.
from typing import Annotated
from pydantic import UUID4, Field, PlainSerializer
from superjwt import Alg, JWTClaims, Validation, decode, encode
from superjwt.exceptions import ClaimsValidationError
secret_key = "your-secret-key-of-len-32-bytes!"
class MyJWTClaims(JWTClaims):
# redefine 'sub' as required integer
sub: int = Field(default=...)
# new custom claim: 'user_id' is required and must be a valid UUIDv4 string
user_id: Annotated[UUID4, PlainSerializer(lambda v: str(v))]
# Example - Validation PASSING
claims = (
MyJWTClaims(sub=123, user_id="b2a4c791-2cf4-4e41-9a20-8532129ff47c")
.with_expiration(minutes=15)
)
compact = encode(claims, secret_key, Alg.HS256)
decoded: MyJWTClaims = decode(compact, secret_key, Alg.HS256, validation=MyJWTClaims)
print(decoded.to_dict())
#> {'sub': 123, 'exp': 1767027591, 'user_id': 'b2a4c791-2cf4-4e41-9a20-8532129ff47c'}
# Example - Validation FAILING
# create an invalid pydantic claims
invalid_claims = (
MyJWTClaims.model_construct(**{"sub": "John Doe", "user_id": "invalid-uuid-string"})
.with_issued_at()
.with_expiration(minutes=10)
)
# disable claims validation to create an "invalid" compact token
invalid_compact = encode(
invalid_claims, secret_key, Alg.HS256, validation=Validation.DISABLE
)
try:
decode(invalid_compact, secret_key, Alg.HS256, validation=MyJWTClaims)
except ClaimsValidationError as e:
print("Claims validation error:", e)
#> Claims validation error: Claims validation failed
# claim ('sub',) = John Doe -> validation failed (int_parsing):
# Input should be a valid integer, unable to parse string as an integer
# claim ('user_id',) = invalid-uuid-string -> validation failed (value_error):
# Value error, badly formed hexadecimal UUID string
Compact Token Inspection
[!CAUTION] When using
inspect(), the JWT is not verified! Never trust the data until it is verified bydecode().
from superjwt import JWSToken, inspect
compact = (
b"eyJhbGciOiJOb05lIiwidHlwIjoiSldUIn0"
b"."
b"eyJjYW5fSV90cnVzdF95b3UiOiJubyJ9"
b"."
b"BsUynvYTk4w4_TCS39qAUoovSmS7hJxG4fahZGK9RrY"
)
token: JWSToken = inspect(compact)
print(token.payload)
#> {'can_I_trust_you': 'no'}
print(token.headers)
#> {'alg': 'NoNe', 'typ': 'JWT'}
Test
-
Clone repository
-
Install dependencies
pip install -e . --group test
-
Run tests
pytest
Metadata
Release files for superjwt 0.8.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| superjwt-0.8.0.tar.gz | 61.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| superjwt-0.8.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 85.8 kB
Release files / superjwt-0.8.0.tar.gz
| Download URL | superjwt-0.8.0.tar.gz |
|---|---|
| Size | 61.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
aad7ed026139608cb3c5218baefc139be300cc08cbd78ad630c9b93f431201d2
|
|
BLAKE2b-256 checksum How to use checksums |
660b1a2b6d056190e6bf9f3c78483b3ed2be383d7d3d8b73244d87da1cab8b14
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jan 29, 2026.
Transparency logRelease files / superjwt-0.8.0-py3-none-any.whl
| Download URL | superjwt-0.8.0-py3-none-any.whl |
|---|---|
| Size | 24.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f84681318b7c6a548bac2020f08dfd9558e7881329ad0c104036c3c7e1d20318
|
|
BLAKE2b-256 checksum How to use checksums |
66415d8b59bd8a9e2ae50e5aec537a1bb388be12b991760087111e2424527e9b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jan 29, 2026.
Transparency log