Skip to main content

A small, opinionated JWT toolkit: create, verify, expire, and handle errors without writing the same PyJWT boilerplate every project.

Project description

fastjwt-kit

A small, opinionated wrapper around PyJWT that handles the boilerplate every project rewrites: token creation, expiry, claim validation, and clean error handling, without importing jwt exceptions directly.

Install

pip install fastjwt-kit

For asymmetric algorithms (RS256, ES256), install the cryptography package as well:

pip install fastjwt-kit cryptography

Quickstart

from fastjwt_kit import create_access_token, verify_token, TokenExpiredError

token = create_access_token(subject="user-123", secret="change-me")

try:
    claims = verify_token(token, secret="change-me")
    print(claims["sub"])  # "user-123"
except TokenExpiredError:
    print("Token expired, ask the user to log in again")

No manual exp math, no catching raw jwt.ExpiredSignatureError.

Token types

create_access_token and create_refresh_token are convenience wrappers that set sensible defaults and inject a type claim:

from fastjwt_kit import create_access_token, create_refresh_token

access  = create_access_token(subject="user-123", secret="change-me")   # 15 min, type: "access"
refresh = create_refresh_token(subject="user-123", secret="change-me")  # 7 days, type: "refresh"

Use the type claim in the decoded payload to distinguish tokens:

claims = verify_token(access, secret="change-me")
assert claims["type"] == "access"

Creating tokens

create_token

Full control over all claims:

import datetime as dt
from fastjwt_kit import create_token

token = create_token(
    subject="user-123",
    secret="change-me",
    expires_in=dt.timedelta(hours=1),
    issuer="my-app",
    audience="my-app-clients",
    extra_claims={"role": "admin"},
)
Parameter Type Required Description
subject str Yes Value of the sub claim, typically a user ID.
secret str Yes Signing secret for HS256, or a private key object/PEM bytes for asymmetric algorithms.
expires_in timedelta Yes How long until the token expires.
algorithm str No Signing algorithm. Defaults to "HS256".
issuer str No Value of the iss claim.
audience str No Value of the aud claim.
extra_claims dict No Additional claims to embed in the payload (for example {"role": "admin"}).

create_access_token / create_refresh_token

Same parameters as create_token, but expires_in has a built-in default:

Function Default expires_in Auto-added claim
create_access_token 15 minutes type: "access"
create_refresh_token 7 days type: "refresh"

Verifying tokens

from fastjwt_kit import verify_token

claims = verify_token(
    token,
    secret="change-me",
    issuer="my-app",
    audience="my-app-clients",
)
Parameter Type Required Description
token str Yes The encoded JWT string.
secret str Yes Signing secret for HS256, or the public key object/PEM bytes for asymmetric algorithms.
algorithm str No Expected signing algorithm. Defaults to "HS256". Must match the algorithm used to sign.
issuer str No If provided, the token's iss claim must match exactly.
audience str No If provided, the token's aud claim must match exactly.

Returns a dict[str, Any] of the decoded claims on success.

Asymmetric algorithms (RS256, ES256)

For cross-service token exchange or anywhere you cannot share a symmetric secret, use an asymmetric algorithm. Pass key objects directly:

import datetime as dt
from cryptography.hazmat.primitives.asymmetric import rsa
from fastjwt_kit import create_token, verify_token

# Generate a key pair once and store securely
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key  = private_key.public_key()

# Sign with the private key
token = create_token(
    subject="user-123",
    secret=private_key,
    expires_in=dt.timedelta(hours=1),
    algorithm="RS256",
)

# Verify with the public key
claims = verify_token(token, secret=public_key, algorithm="RS256")

The algorithm value must match in both create_token and verify_token. Passing a mismatched algorithm to verify_token raises InvalidTokenError.

Error handling

All errors inherit from TokenError:

TokenError
├── TokenExpiredError      token's exp claim has passed
├── InvalidTokenError      malformed, tampered, or wrong algorithm
└── InvalidClaimsError     iss, aud, or other required claims do not match

Catch broadly or specifically:

from fastjwt_kit import TokenError, TokenExpiredError, InvalidTokenError, InvalidClaimsError

try:
    claims = verify_token(token, secret="change-me")
except TokenExpiredError:
    ...  # token was valid but has expired
except InvalidClaimsError:
    ...  # issuer or audience did not match
except InvalidTokenError:
    ...  # malformed or tampered token
except TokenError:
    ...  # catch-all for any token error

You never need to import jwt exceptions directly.

API reference

Functions

Function Returns Description
create_token(subject, secret, *, expires_in, ...) str Create a JWT with full control over all claims.
create_access_token(subject, secret, ...) str Short-lived token (default 15 min), tagged type: "access".
create_refresh_token(subject, secret, ...) str Long-lived token (default 7 days), tagged type: "refresh".
verify_token(token, secret, *, algorithm, issuer, audience) dict[str, Any] Decode and validate a token, raising typed exceptions on failure.

Exceptions

Exception Raised when
TokenError Base class. Catch this to handle any token error.
TokenExpiredError The token's exp claim has passed.
InvalidTokenError The token is malformed, the signature is invalid, or the algorithm does not match.
InvalidClaimsError Required claims (iss, aud) are missing or do not match expected values.

Why not just use PyJWT directly?

You can. This package wraps the parts everyone ends up rewriting:

  • Expiry defaults so you do not hardcode timedelta(minutes=15) in every project
  • iss and aud validation plumbed through without managing options dicts manually
  • Exception types that do not leak the underlying library, so upgrading PyJWT does not break your error handling

If you only need raw encode/decode, PyJWT alone is fine.

Roadmap

  • Password hashing module
  • FastAPI signup/login router and get_current_user dependency
  • Config/env validation helpers

Feedback and issues welcome. This is an early release and will evolve based on real usage.

License

MIT

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

fastjwt_kit-0.2.0.tar.gz (9.5 kB view details)

Uploaded Source

Built Distribution

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

fastjwt_kit-0.2.0-py3-none-any.whl (9.3 kB view details)

Uploaded Python 3

File details

Details for the file fastjwt_kit-0.2.0.tar.gz.

File metadata

  • Download URL: fastjwt_kit-0.2.0.tar.gz
  • Upload date:
  • Size: 9.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.4

File hashes

Hashes for fastjwt_kit-0.2.0.tar.gz
Algorithm Hash digest
SHA256 407b6ff0db2b0494660bd28e4782b323055accdd77a3fcf3c1c18aef2bb89693
MD5 68d6ed12bd1a3f5d77fb87d6647e2555
BLAKE2b-256 896a69da08cc20ba508cfb8d1f858675e43659c32eeb088a458a59937d4d2258

See more details on using hashes here.

File details

Details for the file fastjwt_kit-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: fastjwt_kit-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 9.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.4

File hashes

Hashes for fastjwt_kit-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 81483e07f975bf159a6c771cf2574e6cd8ccef66d904bb08909f2463de78804b
MD5 1ade2a87fddfa9dbb0b3b0a07c962f8f
BLAKE2b-256 171d2f2dd7f84f53f5f24f07e343d9ca790e50fd6bced91b8cb2faf71fd5541c

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