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)
#> {'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)
#> {'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.6.0.tar.gz (56.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.6.0-py3-none-any.whl (26.5 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: superjwt-0.6.0.tar.gz
  • Upload date:
  • Size: 56.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.6.0.tar.gz
Algorithm Hash digest
SHA256 ea7c5e3c012f32a8e4fc368976f92f71f91922826617cd53c726ccad842c7ac3
MD5 9e107432d2cc85be4154706d03f1eb88
BLAKE2b-256 94478abafc3808dcdcb7396ba48a0c99d779d69b2b11501a6d572017b1750f8c

See more details on using hashes here.

Provenance

The following attestation bundles were made for superjwt-0.6.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.6.0-py3-none-any.whl.

File metadata

  • Download URL: superjwt-0.6.0-py3-none-any.whl
  • Upload date:
  • Size: 26.5 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.6.0-py3-none-any.whl
Algorithm Hash digest
SHA256 ec0c443607381accabaeb309c6c09f85313b6d3ff0322b927a262a71570aae4e
MD5 57b62fc76a92a86e1da5a11270dc6d34
BLAKE2b-256 fa068106e7bd6c79fd2fdf5d8abcdab9e8fbff64d8eccfd553b8505ce7779cab

See more details on using hashes here.

Provenance

The following attestation bundles were made for superjwt-0.6.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