A super simple JWT library for Python
Project description
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 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
Test
-
Clone repository
-
Install dependencies
pip install -e . --group test
-
Run tests
pytest
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
61f4a9271dcc2fe37c51a28487a9575d7a94720682c26970d0f11cd2107e1799
|
|
| MD5 |
d3e1df8f7ab17609593ebbce88c52e17
|
|
| BLAKE2b-256 |
213696eff695be71d93024351e7fc433904d07804dfdc4f8ef27613d77bcb303
|
Provenance
The following attestation bundles were made for superjwt-0.3.0.tar.gz:
Publisher:
pypi.yml on ixunio/superjwt
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
superjwt-0.3.0.tar.gz -
Subject digest:
61f4a9271dcc2fe37c51a28487a9575d7a94720682c26970d0f11cd2107e1799 - Sigstore transparency entry: 782925137
- Sigstore integration time:
-
Permalink:
ixunio/superjwt@8180c13b574d69ab13ebc296f5512854d981c24d -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/ixunio
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yml@8180c13b574d69ab13ebc296f5512854d981c24d -
Trigger Event:
release
-
Statement type:
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3529ea38212927848b3975ee61e370ee70e6b228050dfa26e678f5a89e835c62
|
|
| MD5 |
dbc1d7fe5ff3a03f87fc96a73cb43b34
|
|
| BLAKE2b-256 |
deb22053b52109b3f76f03f5d59f78ebd4c8f54a86089ff853c680e3305c5396
|
Provenance
The following attestation bundles were made for superjwt-0.3.0-py3-none-any.whl:
Publisher:
pypi.yml on ixunio/superjwt
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
superjwt-0.3.0-py3-none-any.whl -
Subject digest:
3529ea38212927848b3975ee61e370ee70e6b228050dfa26e678f5a89e835c62 - Sigstore transparency entry: 782925147
- Sigstore integration time:
-
Permalink:
ixunio/superjwt@8180c13b574d69ab13ebc296f5512854d981c24d -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/ixunio
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
pypi.yml@8180c13b574d69ab13ebc296f5512854d981c24d -
Trigger Event:
release
-
Statement type: