Skip to main content

A super simple JWT library for Python

Project description

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 the standard JWT claims.

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)
#> b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
#   .eyJpc3MiOiJteS1hcHAiLCJzdWIiOiJKb2huIERvZSJ9
#   .HwnUqTLFAMzNkMrokd0aI7c-zSJJpSVXMrYIhUyWe4s'

decoded: dict = decode(compact, secret_key, Alg.HS256, claims_validation=JWTClaims)
#> {'iss': 'my-app', '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)
#> b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
#   .eyJpc3MiOiJteS1hcHAiLCJzdWIiOiJKb2huIERvZSIsImlhdCI6MTc2NzAyNzQ4MywiZXhwIjoxNzY3MDI4MzgzfQ
#   .ZXxZT8VzL8IPTov-enslCh57S2M5fQBtqULZx5zEAm8'

decoded: dict = decode(compact, secret_key, Alg.HS256, claims_validation=JWTClaims)
#> {'iss': 'my-app', 'sub': 'John Doe', 'iat': 1767027483, 'exp': 1767028383}

Custom Claims and Validation

Redefine standard claims or define new custom ones. Validate automatically during encoding and decoding.

from typing import Annotated
from uuid import UUID

from pydantic import AfterValidator, Field
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[str, AfterValidator(lambda x: str(UUID(x, version=4)))]
# 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 = decode(compact, secret_key, Alg.HS256, claims_validation=MyJWTClaims)
#> {'sub': 123, 'exp': 1767027591, 'user_id': 'b2a4c791-2cf4-4e41-9a20-8532129ff47c'}
# Example - Validation FAILING

# create a non-validated and 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 default validation to create an invalid token
invalid_compact = encode(
    invalid_claims, secret_key, Alg.HS256, claims_validation=Validation.DISABLE
)
try:
    decode(invalid_compact, secret_key, Alg.HS256, claims_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 .[asymmetric] --group test
    
  3. Run tests

    pytest
    

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

superjwt-0.5.0.tar.gz (55.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

superjwt-0.5.0-py3-none-any.whl (26.3 kB view details)

Uploaded Python 3

File details

Details for the file superjwt-0.5.0.tar.gz.

File metadata

  • Download URL: superjwt-0.5.0.tar.gz
  • Upload date:
  • Size: 55.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for superjwt-0.5.0.tar.gz
Algorithm Hash digest
SHA256 f5f43019d0ec4160167c429ca8e81c9942623c5e2f4a61b221b8f476cade1925
MD5 d53f3767e019ee3c2ce88d43bfc19518
BLAKE2b-256 9b40913642c6023bcf48252886b78682efa7481f627112f96e2b05fbbb12fe0d

See more details on using hashes here.

Provenance

The following attestation bundles were made for superjwt-0.5.0.tar.gz:

Publisher: pypi.yml on ixunio/superjwt

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file superjwt-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: superjwt-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 26.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for superjwt-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0dc207565e2207e6aeb8701426d5493af7a674228fa4c2b4a0bc84b7d4b9912b
MD5 1119013c8dddbe4d9c1bd429a02bb461
BLAKE2b-256 da7e07d0c18fddb87ae04c6eed04c4ff6ce07e52bca469e28148e23d50ac6e70

See more details on using hashes here.

Provenance

The following attestation bundles were made for superjwt-0.5.0-py3-none-any.whl:

Publisher: pypi.yml on ixunio/superjwt

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page