OxyJWT is a Python JWT/JWS library backed by a Rust core. The public API follows PyJWT for encode, decode, decode_complete, JWK/JWKS helpers, and the PyJWKClient. When signature verification is enabled (the default), you must pass an algorithms allow-list, matching common PyJWT usage. Unverified decode is available only when you explicitly set options["verify_signature"] to False (treat the payload as untrusted).
This project is still alpha software; see the changelog for 0.2.0 breaking changes (exception hierarchy).
The full documentation is written with MkDocs and lives in docs-site/ as a standalone site:
Build it locally with:
python -m venv .venv
.venv/bin/python -m pip install -U -r docs-site/requirements.txt
.venv/bin/mkdocs serve -f docs-site/mkdocs.ymlOr build a static documentation image for deployment:
docker compose -f docs-site/docker-compose.yml up -d --buildThe static site is served on http://127.0.0.1:8001 by default. Point your own reverse proxy at that upstream for HTTPS. Details and OXYJWT_DOCS_PORT are in docs-site/README.md.
pip install oxyjwtFor local development:
python -m venv .venv
.venv/bin/python -m pip install -U pip maturin pytest cryptography pyjwt
.venv/bin/maturin develop --release
.venv/bin/python -m pytestimport time
import oxyjwt
secret = "super-secret"
payload = {
"sub": "user-123",
"role": "admin",
"aud": "api",
"iss": "auth-service",
"exp": int(time.time()) + 3600,
}
token = oxyjwt.encode(payload, secret, algorithm="HS256", headers={"kid": "key-1"})
claims = oxyjwt.decode(
token,
secret,
algorithms=["HS256"],
audience="api",
issuer="auth-service",
)Use explicit key constructors for RSA, PSS, ECDSA, and EdDSA:
import oxyjwt
signing_key = oxyjwt.EncodingKey.from_rsa_pem(private_pem)
verification_key = oxyjwt.DecodingKey.from_rsa_pem(public_pem)
token = oxyjwt.encode({"sub": "user-123", "exp": 1893456000}, signing_key, algorithm="RS256")
claims = oxyjwt.decode(token, verification_key, algorithms=["RS256"])Supported algorithms in v1:
HS256,HS384,HS512RS256,RS384,RS512PS256,PS384,PS512ES256,ES384EdDSA
OxyJWT exposes a stable exception hierarchy:
try:
claims = oxyjwt.decode(token, key, algorithms=["HS256"])
except oxyjwt.ExpiredSignatureError:
...
except oxyjwt.InvalidTokenError:
...All package exceptions inherit from oxyjwt.OxyJWTError.
There is a small comparison script for OxyJWT, PyJWT, python-jose, and Authlib:
python -m venv .venv
.venv/bin/python -m pip install -U pip maturin ".[bench]"
.venv/bin/maturin develop --release
.venv/bin/python scripts/compare_jwt_libraries.py \
--algorithms all \
--iterations 1000 \
--rounds 3 \
--warmup 100 \
--json benchmark-results/all-algorithms.bench.json \
--markdown benchmark-results/all-algorithms.bench.mdThe script covers HMAC, RSA, RSA-PSS, ECDSA, and EdDSA algorithms. Unsupported library/algorithm combinations are reported as 0 throughput. For a quicker smoke test, pass something like --algorithms HS256,RS256,EdDSA --iterations 100 --rounds 1.
Benchmark outputs are ignored by git because results depend on the machine, Python version, compiler flags, and CPU state.
The default Rust crypto backend is aws_lc_rs, chosen for stronger performance on RSA and ECDSA in local benchmarks. You can still build with rust_crypto for comparison:
PYO3_BUILD_EXTENSION_MODULE=1 maturin build --release --no-default-features --features rust_crypto- Always pass a fixed server-side
algorithmslist todecode. - Never build the
algorithmslist from untrusted token headers. alg="none"is intentionally unsupported.- Raw
str/byteskeys are accepted only for HMAC algorithms. UseEncodingKey.from_*andDecodingKey.from_*for RSA, PSS, ECDSA, and EdDSA. - Validate
audienceandissuerfor application tokens when those claims are part of your trust model. decode_unverifiedandget_unverified_headerdo not authenticate a token. Use them only for inspection/debugging flows, never for authorization.
OxyJWT implements JWT/JWS signing and verification. JWE encryption is not part of the first version.
OxyJWT is built for absolute speed. By bypassing the Python GIL and leveraging Rust's cryptographic primitives, it completely destroys standard Python libraries in both symmetric and asymmetric cryptography.
Below is a performance comparison measured in Operations per second (ops/sec) (higher is better):
| Algorithm | Operation | ⚡ OxyJWT | PyJWT | Authlib | python-jose |
|---|---|---|---|---|---|
| HS256 | Encode | 620,270 | 140,670 | 99,408 | 99,507 |
| HS256 | Decode | 361,073 | 109,272 | 94,823 | 51,838 |
| RS256 | Encode | 1,934 | 35 | 35 | 35 |
| RS256 | Decode | 58,752 | 27,200 | 26,085 | 23,046 |
| EdDSA | Encode | 69,105 | 17,518 | 15,014 | N/A |
| EdDSA | Decode | 31,666 | 10,741 | 10,317 | N/A |
| ES256 | Encode | 46,559 | 19,632 | 16,199 | 19,723 |
Tested against standard Python ecosystem libraries. OxyJWT consistently dominates across all algorithms.