Skip to main content
SuperJWT full logo
A modern implementation of JSON Web Token (JWT) for Python.
With powerful Pydantic validation features.

GitHub Actions workflow status on main branch

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 by decode().

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'}

See full documentation

Test

  1. Clone repository

  2. Install dependencies

    pip install -e . --group test
    
  3. 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)

Source distribution for superjwt 0.8.0
File Size Uploaded
superjwt-0.8.0.tar.gz 61.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for superjwt 0.8.0
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

This release

0.8.0 This release

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page