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 with HMAC-SHA2 algorithms and includes advanced features like token inspection and detached payload mode.

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 header.

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 🐣

With a dict

from superjwt import encode, decode

secret_key = "your-secret-key-of-len-32-bytes!"

claims = {"iss": "my-app", "sub": "John Doe"}

token: bytes = encode(claims, key=secret_key, algorithm="HS256")
#> token = b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJteS1hcHAiLCJzdWIiOiJKb2huIERvZSJ9.HwnUqTLFAMzNkMrokd0aI7c-zSJJpSVXMrYIhUyWe4s'
decoded: dict = decode(token, key=secret_key, algorithm="HS256")
#> decoded = {'iss': 'my-app', 'sub': 'John Doe'}

With a pydantic model

auto-validate JWT content and seamlessly include 'iat' (Issued At) and 'exp' (Expiration) claims

from superjwt import 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)
)

token: bytes = encode(claims, key=secret_key, algorithm="HS256")
#> token = b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJteS1hcHAiLCJzdWIiOiJKb2huIERvZSIsImlhdCI6MTc2NzAyNzQ4MywiZXhwIjoxNzY3MDI4MzgzfQ.ZXxZT8VzL8IPTov-enslCh57S2M5fQBtqULZx5zEAm8'
decoded: dict = decode(token, key=secret_key, algorithm="HS256", validation_claims=JWTClaims)
#> decoded = {'iss': 'my-app', 'sub': 'John Doe', 'iat': 1767027483, 'exp': 1767028383}

Custom Claims and Validation

from typing import Annotated
from uuid import UUID

from pydantic import AfterValidator, Field
from superjwt import JWTClaims, decode, encode
from superjwt.exceptions import ClaimsValidationError

secret_key = "your-secret-key-of-len-32-bytes!"

# Define a custom pydantic model to validate claims
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)))]

Valid Claims Example

claims = (
    MyJWTClaims(sub=123, user_id="b2a4c791-2cf4-4e41-9a20-8532129ff47c")
    .with_issued_at()
    .with_expiration(minutes=15)
)
token = encode(claims, secret_key, algorithm="HS256")
#> token = b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOjEyMywiaWF0IjoxNzY3MDI2NjkxLCJleHAiOjE3NjcwMjc1OTEsInVzZXJfaWQiOiJiMmE0Yzc5MS0yY2Y0LTRlNDEtOWEyMC04NTMyMTI5ZmY0N2MifQ.kKSdYM5gxZciqtlg3ID5ff1RLUXctmjP18fU0UbjthE'
decoded = decode(
    token=token,
    key=secret_key,
    algorithm="HS256",
    validation_claims=MyJWTClaims,
)
#> decoded = {'sub': 123, 'iat': 1767026691, 'exp': 1767027591, 'user_id': 'b2a4c791-2cf4-4e41-9a20-8532129ff47c'}

Invalid Claims Example

# 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=15)
)
# disable claims validation to create an invalid token
invalid_token = encode(
    invalid_claims, secret_key, algorithm="HS256", validation_claims=None
)
#> invalid_token = b'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJKb2huIERvZSIsImlhdCI6MTc2NzAyNzIxNiwiZXhwIjoxNzY3MDI4MTE2LCJ1c2VyX2lkIjoiaW52YWxpZC11dWlkLXN0cmluZyJ9.eBJKSxoBNWtS7uojVIIUYJWC26c2GHP8o4LPZm41tp8'
try:
    decoded_invalid = decode(
        token=invalid_token,
        key=secret_key,
        algorithm="HS256",
        validation_claims=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

Documentation

See full documentation

Test

  1. Clone repository

  2. Install dependencies

    pip install -e . --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.3.0.tar.gz (22.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.3.0-py3-none-any.whl (16.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: superjwt-0.3.0.tar.gz
  • Upload date:
  • Size: 22.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.3.0.tar.gz
Algorithm Hash digest
SHA256 61f4a9271dcc2fe37c51a28487a9575d7a94720682c26970d0f11cd2107e1799
MD5 d3e1df8f7ab17609593ebbce88c52e17
BLAKE2b-256 213696eff695be71d93024351e7fc433904d07804dfdc4f8ef27613d77bcb303

See more details on using hashes here.

Provenance

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

File metadata

  • Download URL: superjwt-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 16.0 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.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3529ea38212927848b3975ee61e370ee70e6b228050dfa26e678f5a89e835c62
MD5 dbc1d7fe5ff3a03f87fc96a73cb43b34
BLAKE2b-256 deb22053b52109b3f76f03f5d59f78ebd4c8f54a86089ff853c680e3305c5396

See more details on using hashes here.

Provenance

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