Skip to main content

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.

PyPI Python License: MIT

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


Download files

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

Source Distributions

No source distribution files available for this release.See tutorial on generating distribution archives.

Built Distribution

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

crab_jwks-0.1.0-cp313-cp313-win_amd64.whl (447.8 kB view details)

Uploaded CPython 3.13Windows x86-64

File details

Details for the file crab_jwks-0.1.0-cp313-cp313-win_amd64.whl.

File metadata

File hashes

Hashes for crab_jwks-0.1.0-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 958bdf7fb03cc2ad6b5ae7533da597dee1018dd4d758787f4fc13d42f02e85a2
MD5 ad479b45a4dda36c917d89be731f9b05
BLAKE2b-256 2f765711685605a4907c6c8fc23936732a857a3ba19524527f5a969927ef88d4

See more details on using hashes here.

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