Minimal JWT/JWKS library with RS256 support - Python bindings for Rust
Project description
crab-jwt
A minimal, fast JWT/JWKS library for Python with RS256 support, powered by Rust.
Features
- Fast - Core cryptography implemented in Rust
- Secure - Uses audited RSA implementation
- Simple API - Fluent builder pattern for creating tokens
- JWKS Support - Generate and parse JSON Web Key Sets
- Type Hints - Full type annotation support (PEP 561)
Installation
pip install crab-jwt
Quick Start
from crab_jwt import KeyPair, JwtBuilder, verify
# Generate RSA key pair
keys = KeyPair.generate()
# Create a signed JWT
token = (JwtBuilder()
.subject("user123")
.issuer("my-app")
.claim("role", "admin")
.expiration_in(3600) # 1 hour
.sign(keys))
# Verify and decode the token
claims = verify(token, keys.to_public_pem())
print(claims.sub) # "user123"
print(claims.get("role")) # "admin"
Usage
Creating Tokens
Use the builder pattern to construct JWT claims:
from crab_jwt import KeyPair, JwtBuilder
keys = KeyPair.generate()
token = (JwtBuilder()
.subject("user-id-123") # sub claim
.issuer("https://auth.example.com") # iss claim
.audience("https://api.example.com") # aud claim
.expiration_in(3600) # exp: now + 1 hour
.issued_now() # iat: current timestamp
.not_before(1234567890) # nbf claim
.jwt_id("unique-token-id") # jti claim
.claim("role", "admin") # custom claim
.claim("permissions", ["read", "write"]) # custom claim
.sign(keys))
Verifying Tokens
from crab_jwt import verify
claims = verify(token, public_pem)
# Access standard claims
print(claims.sub) # Subject
print(claims.iss) # Issuer
print(claims.exp) # Expiration timestamp
# Access custom claims
role = claims.get("role")
permissions = claims.get("permissions")
# Get all custom claims as dict
custom = claims.custom_claims()
Time Validation
Time validation is opt-in:
claims = verify(token, public_pem)
# Check if token has expired
if claims.is_expired():
raise Exception("Token has expired")
# Check both exp and nbf
if not claims.is_valid_time():
raise Exception("Token is not valid at this time")
Key Management
Generate new keys:
from crab_jwt import KeyPair
keys = KeyPair.generate()
Export to PEM:
private_pem = keys.to_private_pem()
public_pem = keys.to_public_pem()
# Save to files
with open("private.pem", "w") as f:
f.write(private_pem)
Import from PEM:
with open("private.pem") as f:
keys = KeyPair.from_private_pem(f.read())
JWKS (JSON Web Key Set)
Generate JWKS for public key distribution:
from crab_jwt import Jwks, KeyPair
keys = KeyPair.generate()
# Create JWKS
jwks = Jwks.from_public_key(keys.to_public_pem(), "key-2024-01")
# Serialize to JSON (for your /.well-known/jwks.json endpoint)
json_str = jwks.to_json()
Multiple keys (for key rotation):
jwks = Jwks()
jwks.add_key(current_key.to_public_pem(), "key-2024-02")
jwks.add_key(previous_key.to_public_pem(), "key-2024-01")
print(jwks.key_ids()) # ["key-2024-02", "key-2024-01"]
Verify with JWKS:
from crab_jwt import Jwks, verify_with_jwks
# Parse JWKS (e.g., fetched from HTTP)
jwks = Jwks.from_json(jwks_json)
# Verify token using key ID
claims = verify_with_jwks(token, jwks, "key-2024-01")
API Reference
KeyPair
| Method | Description |
|---|---|
KeyPair.generate() |
Generate new 2048-bit RSA key pair |
KeyPair.from_private_pem(pem) |
Import from PEM string |
.to_private_pem() |
Export private key to PEM |
.to_public_pem() |
Export public key to PEM |
JwtBuilder
| Method | Description |
|---|---|
.subject(sub) |
Set subject claim |
.issuer(iss) |
Set issuer claim |
.audience(aud) |
Set audience claim |
.expiration(timestamp) |
Set expiration (Unix timestamp) |
.expiration_in(seconds) |
Set expiration relative to now |
.issued_at(timestamp) |
Set issued-at timestamp |
.issued_now() |
Set issued-at to current time |
.not_before(timestamp) |
Set not-before timestamp |
.jwt_id(jti) |
Set unique token ID |
.claim(key, value) |
Add custom claim |
.sign(keys) |
Sign and return token string |
Claims
| Property/Method | Description |
|---|---|
.sub |
Subject claim |
.iss |
Issuer claim |
.aud |
Audience claim |
.exp |
Expiration timestamp |
.iat |
Issued-at timestamp |
.nbf |
Not-before timestamp |
.jti |
JWT ID |
.is_expired() |
Check if token has expired |
.is_valid_time() |
Check exp and nbf validity |
.get(key) |
Get custom claim value |
.custom_claims() |
Get all custom claims as dict |
Jwks
| Method | Description |
|---|---|
Jwks() |
Create empty JWKS |
Jwks.from_public_key(pem, kid) |
Create JWKS with one key |
Jwks.from_json(json) |
Parse JWKS from JSON |
.add_key(pem, kid) |
Add a key to the set |
.key_ids() |
Get list of key IDs |
.to_json() |
Serialize to JSON |
Functions
| Function | Description |
|---|---|
verify(token, public_pem) |
Verify token with public key PEM |
verify_with_jwks(token, jwks, kid) |
Verify token using JWKS |
Error Handling
All errors raise ValueError with descriptive messages:
from crab_jwt import verify
try:
claims = verify(token, public_pem)
except ValueError as e:
if "invalid signature" in str(e):
print("Signature verification failed")
elif "invalid token" in str(e):
print("Malformed token")
else:
print(f"Error: {e}")
Performance
crab-jwt is powered by Rust, providing significant performance benefits over pure Python implementations for cryptographic operations.
License
MIT License
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 Distributions
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 crab_jwks-0.1.0-cp313-cp313-win_amd64.whl.
File metadata
- Download URL: crab_jwks-0.1.0-cp313-cp313-win_amd64.whl
- Upload date:
- Size: 447.8 kB
- Tags: CPython 3.13, Windows x86-64
- Uploaded using Trusted Publishing? No
- Uploaded via: maturin/1.10.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
958bdf7fb03cc2ad6b5ae7533da597dee1018dd4d758787f4fc13d42f02e85a2
|
|
| MD5 |
ad479b45a4dda36c917d89be731f9b05
|
|
| BLAKE2b-256 |
2f765711685605a4907c6c8fc23936732a857a3ba19524527f5a969927ef88d4
|