Skip to main content

A modern Python library for issuing and verifying limited-time, scoped, and replay-protected tokens.

Project description

🪙 Tokenmint

PyPI version Python versions License

Tokenmint is a modern Python library for issuing and verifying limited-time, scoped, and replay-protected tokens. Built for 2025 and beyond, it provides a production-grade token system with features like key rotation, purpose scoping, Redis-backed replay prevention, and secure Ed25519 signatures.

✨ Features

  • 🔑 Key rotation with kid headers for zero-downtime upgrades
  • Limited-time tokens (short-lived, scoped, expiring)
  • 🛡 Replay protection using Redis-backed JTI tracking
  • 🎯 Strict purpose scoping (e.g., email-verify, password-reset)
  • 🖋 EdDSA (Ed25519) signatures for modern cryptographic security
  • 🏗 Production-ready: structured errors, leeway for clock skew, revocation support
  • ⚡ Works with Django, FastAPI, Flask, or standalone services

📦 Installation

You can install Tokenmint via pip or uv:

  • Using uv
uv add tokenmint
  • Using pip
pip install tokenmint

🚀 Quick Start

from datetime import timedelta
from tokenmint.settings import Settings
from tokenmint.services import TokenMint

# Define token settings
settings = Settings(
    issuer="myapp.io",
    audience="myapp.web",
    purpose="email-verify",
    expiry_duration=timedelta(minutes=15),
)

# Create token mint (auto-loads keys from environment, uses Redis for replay prevention)
mint = TokenMint(settings)

# Generate a token
token = mint.generate_token(
    subject_id="user:42",
    extra_claims={"email": "alice@example.com"},
)

print("Issued token:", token)

# Validate a token
claims = mint.validate_token(token)
print("Decoded claims:", claims)

🔐 Key Management

tokenmint uses Ed25519 key pairs with support for key rotation. Keys are loaded from environment variables:

export TOKEN_ACTIVE_KEY_ID="2025-08-rot-1"
export TOKEN_PRIVATE_KEY_2025-08-rot-1="$(cat ed25519-private.pem)"
  • Rotate by adding a new private key (TOKEN_PRIVATE_KEY_<new_id>)
  • Update TOKEN_ACTIVE_KEY_ID to the new key id
  • Old tokens remain valid until expiry since their public keys are still available

📖 Detailed Use Cases

tokenmint is designed to fit common security-sensitive flows. Below are example implementations.

1. 🔑 Email Verification

Send a time-limited verification link when a user signs up:

from datetime import timedelta
from tokenmint.settings import Settings
from tokenmint.services import TokenMint

settings = Settings(
    issuer="myapp.io",
    audience="myapp.web",
    purpose="email-verify",
    expiry_duration=timedelta(minutes=10),
)

mint = TokenMint(settings)

# Issue a token for user
token = mint.generate_token(
    subject_id="user:42",
    extra_claims={"email": "alice@example.com"},
)

# Send via email link
verification_url = f"https://myapp.io/verify-email?token={token}"
print("Verification URL:", verification_url)

# Later, when user clicks the link
claims = mint.validate_token(token)
print("Verified email:", claims["email"])

✔ Prevents token replay ✔ Short lifetime ✔ Scoped to "email-verify"

2. 🔒 Password Reset

settings = Settings(
    issuer="myapp.io",
    audience="myapp.web",
    purpose="password-reset",
    expiry_duration=timedelta(minutes=5),
)

mint = TokenMint(settings)

# Issue reset token
reset_token = mint.generate_token(subject_id="user:42")

# User submits new password with token
claims = mint.validate_token(reset_token)

# Force revoke once used
mint.revoke_token(reset_token)

✔ Ensures token can’t be reused ✔ Scoped only for password reset flow

3. 📩 Magic Login Links

No passwords — just a one-time-use link:

settings = Settings(
    issuer="myapp.io",
    audience="myapp.web",
    purpose="magic-login",
    expiry_duration=timedelta(minutes=2),
)

mint = TokenMint(settings)

login_token = mint.generate_token(subject_id="user:42")

# When clicked
claims = mint.validate_token(login_token)
print("User logged in:", claims["sub"])

✔ Very short-lived ✔ One-time login enforcement via replay cache

4. ⚙️ Scoped API Access

Issue short-lived tokens for microservice-to-microservice communication:

settings = Settings(
    issuer="auth.myapp.io",
    audience="payments.myapp.io",
    purpose="service-access",
    expiry_duration=timedelta(minutes=1),
)

mint = TokenMint(settings)

api_token = mint.generate_token(subject_id="service:frontend")

# Receiving service validates
claims = mint.validate_token(api_token)
print("Authorized service:", claims["sub"])

✔ Audience restriction ✔ Purpose scoping ensures the token is useless elsewhere

5. 🎟 One-Time Session Tokens

Great for single-use sensitive operations (like transferring funds):

settings = Settings(
    issuer="myapp.io",
    audience="myapp.api",
    purpose="txn-approval",
    expiry_duration=timedelta(minutes=3),
    prevent_replay=True,
)

mint = TokenMint(settings)

txn_token = mint.generate_token(
    subject_id="user:42",
    extra_claims={"amount": "100.00", "currency": "USD"},
)

# Validate (once only)
claims = mint.validate_token(txn_token)

# Reuse attempt → raises InvalidTokenError
mint.validate_token(txn_token)

6. 🏗 One-Time Payment Approval Token

Scenario: Your app allows users to approve payments securely. Each token must:

  • Be valid only for a single payment request
  • Expire quickly (e.g., 3 minutes)
  • Include extra claims like amount and currency
  • Prevent token replay using Redis
  • Support production-grade key rotation

Environment Setup

export TOKEN_ACTIVE_KEY_ID="2025-08-rot-1"
export TOKEN_PRIVATE_KEY_2025-08-rot-1="$(cat prod-ed25519-rot1.pem)"
export TOKEN_PRIVATE_KEY_2025-05-rot-0="$(cat prod-ed25519-rot0.pem)"
export REDIS_URL="redis://prod-redis-server:6379/3"

Define Token Settings

from datetime import timedelta
from tokenmint.settings import Settings

settings = Settings(
    issuer="payments.myapp.io",
    audience="payments.api",
    purpose="payment-approval",     # scoped to payment approval
    expiry_duration=timedelta(minutes=3),
    clock_skew_leeway=5,            # allow 5 seconds for clock differences
    prevent_replay=True,            # single-use enforcement
)

Configure Redis Replay Cache

from tokenmint.cache import ReplayCache
import os

redis_url = os.getenv("REDIS_URL")
replay_cache = ReplayCache(redis_url=redis_url, key_prefix="prod:payment:jti:")

✅ This isolates payment tokens in Redis to prevent collisions with other token types.

Initialize TokenMint

from tokenmint.services import TokenMint

mint = TokenMint(
    settings=settings,
    replay_cache=replay_cache,
)

Generate Payment Approval Token

payment_token = mint.generate_token(
    subject_id="user:42",
    extra_claims={"amount": "250.00", "currency": "USD", "payment_id": "txn_1001"}
)

print("Send this token to the frontend for approval:", payment_token)

✅ Includes amount, currency, and payment_id claims, ensuring the token is specific to one payment.

Validate Token During Approval

from tokenmint.exceptions import InvalidTokenError

try:
    claims = mint.validate_token(payment_token)
    print("Payment approved for:", claims["sub"])
    print("Amount:", claims["amount"], claims["currency"])
except InvalidTokenError as e:
    print("Invalid or reused token:", str(e))

✅ Checks signature, issuer, audience, expiry, and prevents reuse.

Revoke Token Explicitly (Optional)

mint.revoke_token(payment_token)

✅ Ensures a token can never be reused even before natural expiry.

Production Notes

  • Use dedicated Redis DBs or key prefixes for different token types (prod:payment:jti:).
  • Rotate keys safely with old keys kept in KeyStore for verification of existing tokens.
  • ReplayCache TTL matches token expiry, no manual cleanup needed.
  • Purpose scoping (payment-approval) prevents tokens from being misused for other actions.

🔑 Build Any Tokenized Flow with Replay Protection

tokenmint enforces true single-use tokens with Redis-backed replay prevention.
With these building blocks, you can implement any tokenized flow - from user onboarding to secure inter-service communication.

🤝 Contributing

Contributions are welcome! Please open an issue or PR for any improvements.

📜 License

MIT License — See LICENSE.

👤 Author

Shailesh Pandit

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

tokenmint-0.1.0.tar.gz (21.8 kB view details)

Uploaded Source

Built Distribution

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

tokenmint-0.1.0-py3-none-any.whl (13.9 kB view details)

Uploaded Python 3

File details

Details for the file tokenmint-0.1.0.tar.gz.

File metadata

  • Download URL: tokenmint-0.1.0.tar.gz
  • Upload date:
  • Size: 21.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.7.6

File hashes

Hashes for tokenmint-0.1.0.tar.gz
Algorithm Hash digest
SHA256 d5404d0a26ccf7af15cb8b790559f76732a28692012de58b39759284255bd6e0
MD5 255549fcc1d296092613a564f5dabfc5
BLAKE2b-256 55e98aff08d2b65ae2c5746a3ce2ecf061b7424cde44e617463b664e917c93e9

See more details on using hashes here.

File details

Details for the file tokenmint-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: tokenmint-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 13.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.7.6

File hashes

Hashes for tokenmint-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 de89694f46d4233fd95437f1a7da5f190c8a02e0ff8e968a7393f54416d785dc
MD5 e7d11f39965c12db39bc4eade8ca7da3
BLAKE2b-256 e64f262360b46515c056af63ac1e727ff772ae35bb5cf1359272e0d1b8535fc0

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