Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
d8e9635
perf(decode): gate RFC 7797 header peek on empty payload segment (#57)
ZhuchkaTriplesix May 22, 2026
9c678d9
perf(decode): single-parse unverified decode path (#58)
ZhuchkaTriplesix May 22, 2026
860ac7b
security(api): cap detached_payload size at 256 KiB (#59)
ZhuchkaTriplesix May 22, 2026
6691712
Merge pull request #73 from QueryaHub/issue/57-decode-header-gate
ZhuchkaTriplesix May 22, 2026
b233dc3
Merge pull request #74 from QueryaHub/issue/58-unverified-single-parse
ZhuchkaTriplesix May 22, 2026
6a790ed
Merge pull request #75 from QueryaHub/issue/59-detached-payload-max-b…
ZhuchkaTriplesix May 22, 2026
68aafd0
security(rust): strict compact JWT segment check before decode (#60)
ZhuchkaTriplesix May 22, 2026
1c0302a
ci: run full CI on pushes to dev (#62)
ZhuchkaTriplesix May 22, 2026
05fb672
ci: gate PyPI release workflow on full CI (#61)
ZhuchkaTriplesix May 22, 2026
6e16cba
Merge pull request #76 from QueryaHub/issue/60-strict-compact-segments
ZhuchkaTriplesix May 22, 2026
3b63d3f
Merge pull request #77 from QueryaHub/issue/62-ci-on-dev-push
ZhuchkaTriplesix May 22, 2026
bd684ad
Merge branch 'dev' into issue/61-release-ci-gate
ZhuchkaTriplesix May 22, 2026
c3b4d30
Merge pull request #78 from QueryaHub/issue/61-release-ci-gate
ZhuchkaTriplesix May 22, 2026
8bf7958
perf(encode): use native encode when no custom JSON encoder (#63)
ZhuchkaTriplesix May 22, 2026
67a365b
Merge pull request #79 from QueryaHub/issue/63-encode-single-json-pass
ZhuchkaTriplesix May 22, 2026
8d5c736
perf(rust): single detach in decode_verified_complete (#64)
ZhuchkaTriplesix May 22, 2026
aba0544
perf(api): skip redundant Python aud/iss/sub after Rust (#65)
ZhuchkaTriplesix May 22, 2026
c6ae813
Merge pull request #80 from QueryaHub/issue/64-decode-verified-perf
ZhuchkaTriplesix May 22, 2026
e960c95
Merge pull request #81 from QueryaHub/issue/65-dedupe-claim-validation
ZhuchkaTriplesix May 22, 2026
10cbb9c
perf(rust): validate RFC 7797 detached claims in Rust (#66)
ZhuchkaTriplesix May 22, 2026
b476fb9
fix(api): use InvalidIssuerError when iss missing with issuer= (#69)
ZhuchkaTriplesix May 22, 2026
ffac2db
fix(api): always run issuer check when issuer= is passed (#69)
ZhuchkaTriplesix May 22, 2026
69f8715
fix(jwks): thread-safe PyJWKClient cache (#67)
ZhuchkaTriplesix May 22, 2026
507e0a8
perf(jwks): lazy PyJWK materialization for large JWKS (#68)
ZhuchkaTriplesix May 22, 2026
cf7c214
Merge pull request #82 from QueryaHub/issue/66-detached-rust-claims
ZhuchkaTriplesix May 22, 2026
67c9f2e
Merge pull request #83 from QueryaHub/issue/69-issuer-parity-errors
ZhuchkaTriplesix May 22, 2026
c989451
Merge pull request #84 from QueryaHub/issue/67-jwks-client-thread-safe
ZhuchkaTriplesix May 22, 2026
3a8712f
Merge pull request #85 from QueryaHub/issue/68-jwks-lazy-materialization
ZhuchkaTriplesix May 22, 2026
e3c96af
test: expand security regression contract for 0.5.0 (#72)
ZhuchkaTriplesix May 22, 2026
7a970d9
ci(bench): stabilize HS256 smoke benchmark (#71)
ZhuchkaTriplesix May 22, 2026
ca191ac
docs(bench): document PEM vs cached competitor key modes (#70)
ZhuchkaTriplesix May 22, 2026
b7c1784
Merge pull request #86 from QueryaHub/issue/72-security-regression-ex…
ZhuchkaTriplesix May 22, 2026
a3968c6
Merge pull request #87 from QueryaHub/issue/71-bench-smoke-stability
ZhuchkaTriplesix May 22, 2026
5d9d973
Merge pull request #88 from QueryaHub/issue/70-bench-methodology-docs
ZhuchkaTriplesix May 22, 2026
192c588
chore(release): prepare 0.5.0 on dev
ZhuchkaTriplesix May 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .github/RELEASE_NOTES_v0.5.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# OxyJWT 0.5.0

**Beta** performance and hardening release — faster encode/decode hot paths, JWKS concurrency fixes, stricter compact JWT handling, and expanded security regression coverage. No intentional breaking changes to the public `__all__` API.

## Highlights

### Performance

- Verified decode avoids unconditional header parse unless RFC 7797 detached form (`b64: false`, empty payload segment)
- Single-parse unverified decode; native encode when no custom `json_encoder`
- Rust `decode_verified_complete`: fewer key clones, one detach path; RFC 7797 claims validated in Rust
- Skip redundant Python `aud` / `iss` / `sub` checks after Rust validation
- Lazy `PyJWK` / `DecodingKey` materialization for large JWKS sets

### Security

- `detached_payload` capped at 256 KiB (RFC 7797)
- Unified strict compact JWT segment validation before decode
- Thread-safe `PyJWKClient` cache under concurrent `get_signing_key`
- Expanded `tests/test_security_regression.py` contract (always-on in CI)

### Fixes

- `issuer=` now always validates `iss`; missing `iss` raises `InvalidIssuerError` (PyJWT parity)

### CI & benchmarks

- Full CI on pushes to `dev`; PyPI release workflow gated on passing CI
- HS256 smoke benchmark: 3 rounds, median ratio vs PyJWT (≥75% gate)
- Docs: PEM vs cached competitor key modes for fair asymmetric comparisons

## Install

```bash
pip install oxyjwt==0.5.0
```

## Upgrade from 0.4.0

```bash
pip install -U oxyjwt
```

- No intentional breaking changes to public symbols.
- Stricter validation on malformed compact JWTs and oversized detached payloads.
- When passing `issuer=`, a token without `iss` now fails with `InvalidIssuerError` (was inconsistent before).

See the full [changelog](https://github.com/QueryaHub/OxyJWT/blob/main/docs-site/docs/changelog.md#050--2026-05-22).
14 changes: 12 additions & 2 deletions .github/workflows/benchmarks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
- name: HS256 smoke regression (same gate as main CI)
run: .venv/bin/python -m pytest tests/test_benchmark_jwt_libraries.py::test_benchmark_hs256_smoke_vs_competitors -q

- name: Run full comparison script
- name: Run full comparison script (default pem keys)
run: |
mkdir -p benchmark-results
.venv/bin/python scripts/compare_jwt_libraries.py \
Expand All @@ -47,7 +47,17 @@ jobs:
--warmup 50 \
--markdown benchmark-results/ci-bench.md

- name: Run asymmetric comparison (cached competitor keys)
run: |
.venv/bin/python scripts/compare_jwt_libraries.py \
--algorithms RS256,EdDSA \
--iterations 200 \
--rounds 2 \
--warmup 50 \
--competitor-key-mode cached \
--markdown benchmark-results/ci-bench-cached.md

- uses: actions/upload-artifact@v4
with:
name: benchmark-md
path: benchmark-results/ci-bench.md
path: benchmark-results/
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: CI
on:
pull_request:
push:
branches: [main]
branches: [main, dev]

jobs:
test:
Expand Down
8 changes: 7 additions & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# PyPI: configure Trusted Publishing in https://pypi.org
# and add a GitHub Environment named "pypi" in this repository.
# Release: push an annotated or lightweight tag "v*". Example: git tag -a v0.4.0 -m "Release 0.4.0" && git push origin v0.4.0
# Release: push an annotated or lightweight tag "v*". Example: git tag -a v0.5.0 -m "Release 0.5.0" && git push origin v0.5.0
name: Release

on:
Expand All @@ -18,7 +18,11 @@ permissions:
id-token: write

jobs:
ci-gate:
uses: ./.github/workflows/ci.yml

linux:
needs: [ci-gate]
runs-on: ubuntu-latest
strategy:
fail-fast: false
Expand Down Expand Up @@ -48,6 +52,7 @@ jobs:
path: dist

macos:
needs: [ci-gate]
runs-on: macos-latest
strategy:
matrix:
Expand All @@ -69,6 +74,7 @@ jobs:
path: dist

windows:
needs: [ci-gate]
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
Expand Down
29 changes: 15 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

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 **beta** software on the `0.4.x` line; see the [changelog](docs-site/docs/changelog.md) for **0.2.0** breaking changes (exception hierarchy) and **0.4.0** production-hardening notes.
This project is **beta** software on the `0.5.x` line; see the [changelog](docs-site/docs/changelog.md) for **0.2.0** breaking changes (exception hierarchy), **0.4.0** production-hardening, and **0.5.0** performance notes.

## Documentation

Expand Down Expand Up @@ -129,6 +129,8 @@ python -m venv .venv

The 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 fairness:** the default `--competitor-key-mode pem` keeps pre-parsed `EncodingKey`/`DecodingKey` for OxyJWT while competitors often receive PEM bytes (see [Benchmarks](docs-site/docs/benchmarks.md)). For asymmetric comparisons, also run with `--competitor-key-mode cached`.

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:
Expand All @@ -152,20 +154,19 @@ OxyJWT implements JWT/JWS signing and verification. JWE encryption is not part o

See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup and pull request expectations. Report security issues privately via [SECURITY.md](SECURITY.md).

## 🚀 Performance Benchmarks
## Performance benchmarks

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.
OxyJWT is optimized for throughput on typical JWT workloads (especially HMAC). See [docs-site/docs/benchmarks.md](docs-site/docs/benchmarks.md) for smoke vs extended vs full workflows and key-preparation modes.

Below is a performance comparison measured in **Operations per second (ops/sec)** (higher is better):
The table below is a **historical snapshot** (default script settings, `pem` competitor keys). RS256 encode numbers are not comparable to `--competitor-key-mode cached`; re-run the script on your hardware before drawing conclusions.

| 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 |
| Algorithm | Operation | OxyJWT | PyJWT | Authlib | python-jose |
| :--- | :--- | ---: | ---: | ---: | ---: |

*Tested against standard Python ecosystem libraries. OxyJWT consistently dominates across all algorithms.*
| **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 |
18 changes: 12 additions & 6 deletions RELEASING.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Releasing OxyJWT

This checklist is for maintainers publishing **0.4.x** (and later) to PyPI via the GitHub Actions [Release workflow](.github/workflows/release.yml).
This checklist is for maintainers publishing **0.5.x** (and later) to PyPI via the GitHub Actions [Release workflow](.github/workflows/release.yml).

## Before tagging

Expand All @@ -20,6 +20,12 @@ This checklist is for maintainers publishing **0.4.x** (and later) to PyPI via t
mkdocs build --strict -f docs-site/mkdocs.yml
```

Optional extended benchmark (slower, not required for every PR):

```bash
OXYJWT_BENCHMARK=1 python -m pytest -m benchmark tests/test_benchmark_jwt_libraries.py
```

4. **Smoke import** (after `maturin develop`):

```bash
Expand All @@ -35,16 +41,16 @@ This checklist is for maintainers publishing **0.4.x** (and later) to PyPI via t
## Publish

1. Commit all release-prep changes on `main`.
2. Merge `dev` → `main`, then create and push an annotated tag (example for **0.4.0**):
2. Merge `dev` → `main`, then create and push an annotated tag (example for **0.5.0**):

```bash
git tag -a v0.4.0 -m "Release 0.4.0"
git push origin v0.4.0
git tag -a v0.5.0 -m "Release 0.5.0"
git push origin v0.5.0
```

3. The **Release** workflow builds wheels (Linux x86_64/aarch64, macOS, Windows) + sdist and publishes to PyPI (requires the `pypi` environment and [Trusted Publishing](https://docs.pypi.org/trusted-publishers/)).
3. The **Release** workflow runs full [CI](.github/workflows/ci.yml) via `workflow_call`, then builds wheels (Linux x86_64/aarch64, macOS, Windows) + sdist and publishes to PyPI only if CI passes (requires the `pypi` environment and [Trusted Publishing](https://docs.pypi.org/trusted-publishers/)).

4. On GitHub, create a **Release** from the tag. Use [`.github/RELEASE_NOTES_v0.4.0.md`](.github/RELEASE_NOTES_v0.4.0.md) or the **0.4.0** section in `docs-site/docs/changelog.md` as the release notes body.
4. On GitHub, create a **Release** from the tag. Use [`.github/RELEASE_NOTES_v0.5.0.md`](.github/RELEASE_NOTES_v0.5.0.md) or the **0.5.0** section in `docs-site/docs/changelog.md` as the release notes body.

## After release

Expand Down
5 changes: 3 additions & 2 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@

| Version line | Support |
| --- | --- |
| **0.4.x** (current) | Security fixes and patch releases |
| **0.5.x** (current) | Security fixes and patch releases |
| **0.4.x** | Best-effort backports only for critical issues |
| **0.3.x** | Best-effort backports only for critical issues |
| **0.2.x** and older | Unsupported |

Security fixes target the latest **0.4.x** release. See the [changelog](docs-site/docs/changelog.md) for release history.
Security fixes target the latest **0.5.x** release. See the [changelog](docs-site/docs/changelog.md) for release history.

## Reporting a vulnerability

Expand Down
23 changes: 20 additions & 3 deletions docs-site/docs/benchmarks.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ OxyJWT is optimized for throughput on typical JWT workloads. Numbers depend on C

## Reference ratios (HS256 smoke parameters)

Measured on a typical Linux dev machine with `maturin develop --release`, 50 iterations, 1 round, warmup 8 (same as CI smoke):
Measured on a typical Linux dev machine with `maturin develop --release`, 50 iterations, 3 rounds (median timing), warmup 8 (CI smoke):

| Operation | OxyJWT (ops/s) | PyJWT (ops/s) | OxyJWT / PyJWT |
|-----------|----------------|---------------|----------------|
| encode | ~400k+ | ~130k+ | ~3× |
| decode | ~160k+ | ~115k+ | ~1.4× |

CI asserts **≥75%** of PyJWT for both operations so large regressions fail without requiring absolute ops/s parity across runners.
CI asserts **≥75%** of PyJWT (median ops/s across rounds) for both operations so large regressions fail without requiring absolute ops/s parity across runners.

## Running comparisons locally

Expand All @@ -37,6 +37,17 @@ python3 -m venv .venv
--markdown benchmark-results/local.bench.md
```

Fairer RSA/EdDSA comparison against PyJWT (cached competitor keys):

```bash
.venv/bin/python scripts/compare_jwt_libraries.py \
--algorithms RS256,EdDSA \
--iterations 1000 \
--rounds 3 \
--competitor-key-mode cached \
--markdown benchmark-results/local-cached.bench.md
```

Raw JSON/Markdown outputs are gitignored; keep them local or attach them to release notes as needed.

## CI artifacts
Expand All @@ -49,6 +60,12 @@ Main [CI](https://github.com/QueryaHub/OxyJWT/blob/main/.github/workflows/ci.yml

- **Metric:** operations per second (encode and decode measured separately).
- **Warmup:** reduces JIT and allocator noise; see script defaults.
- **Fairness:** each library uses its supported key types; unsupported pairs are recorded as zero throughput in the script output.
- **Key preparation (`--competitor-key-mode`):**
- **`pem` (default)** — used by CI smoke and the extended pytest sweep. The harness builds one signing/verification key per library before timing. OxyJWT uses `EncodingKey` / `DecodingKey` parsed from PEM once; PyJWT, Authlib, and python-jose receive PEM `str`/`bytes` and may parse that material inside each timed call. This matches “pass a PEM string to the library” usage but can understate competitor throughput on RSA/EC/EdDSA.
- **`cached`** — competitors that support it receive preloaded `cryptography` key objects (same idea as holding parsed keys in application code). Use this for fairer asymmetric comparisons and for release-note / weekly benchmark artifacts.
- **HMAC (HS\*)** — both modes pass the same raw secret; key-mode differences are negligible.
- Unsupported library/algorithm pairs are recorded as zero throughput in the script output.

For asymmetric algorithms, prefer reporting **both** modes or explicitly label which mode was used. The headline table in the root README was measured with defaults that favor OxyJWT on RSA unless noted otherwise.

Always compare on your own target hardware before choosing a library for production latency budgets.
43 changes: 43 additions & 0 deletions docs-site/docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,49 @@

(No changes yet.)

## 0.5.0 — 2026-05-22

Performance and hardening release: faster encode/decode hot paths, JWKS concurrency fixes, stricter compact JWT validation, PyJWT-aligned issuer errors, and expanded security regression tests. The API remains pre-1.0 (Beta). See [Versioning](versioning.md) and [`SECURITY.md` on GitHub](https://github.com/QueryaHub/OxyJWT/blob/main/SECURITY.md).

### Upgrading from 0.4.0

```bash
pip install -U oxyjwt
```

- No intentional breaking changes to the public `__all__` surface.
- Malformed compact JWTs (wrong segment count) are rejected consistently in Rust before decode.
- `detached_payload` is capped at **256 KiB** (RFC 7797).
- When `issuer=` is passed, a token **without** `iss` now raises **`InvalidIssuerError`** (PyJWT parity; previously could slip through on some paths).

### Fixed

- `issuer=` always runs issuer validation in Python; missing `iss` raises `InvalidIssuerError` instead of being skipped.
- `PyJWKClient` JWKS / signing-key cache is thread-safe under concurrent `get_signing_key` (lock around cache mutations).

### Security

- `detached_payload` size capped at 256 KiB before attach (RFC 7797).
- Compact JWT segment validation unified in Rust (reject extra/missing segments before `jwt` parse).
- Expanded `tests/test_security_regression.py` — oversized JWT, detached cap, `none` alg, concurrent JWKS client, issuer-without-iss.

### Performance

- Verified decode: skip `get_unverified_header` unless empty payload segment (RFC 7797 detached form).
- Unverified decode / `get_unverified_header`: single native parse path (no double segment split).
- `encode`: use Rust `encode` directly when no custom `json_encoder` (no `encode_json` round-trip).
- `decode_verified_complete`: hold decoding key by reference; combine detach + verify in one Rust path.
- Skip redundant Python `aud` / `iss` / `sub` validation when Rust already validated on verified decode.
- RFC 7797 verified path: `exp` / `nbf` / `iat` validated in Rust for detached tokens.
- `PyJWK` / `PyJWKSet`: lazy `DecodingKey` materialization; large JWKS sets avoid upfront parse of every key.

### CI & documentation

- Full CI runs on pushes to `dev` (same gates as PRs).
- PyPI [Release workflow](https://github.com/QueryaHub/OxyJWT/blob/main/.github/workflows/release.yml) runs CI via `workflow_call` before publishing.
- HS256 smoke benchmark: 3 rounds, **median** ops/s vs PyJWT; gate remains ≥75%.
- [Benchmarks](benchmarks.md): document smoke / extended / full tiers and PEM vs `cached` competitor key modes.

## 0.4.0 — 2026-05-22

Production hardening release: security fixes, performance improvements, expanded PyJWT/JWKS parity, public typing stubs, and stricter CI. The API remains pre-1.0 (Beta). See [Versioning](versioning.md) and [`SECURITY.md` on GitHub](https://github.com/QueryaHub/OxyJWT/blob/main/SECURITY.md).
Expand Down
2 changes: 1 addition & 1 deletion docs-site/docs/index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# OxyJWT

OxyJWT is a Python JWT/JWS library with a Rust implementation underneath. It gives Python code a **PyJWT-compatible** `encode` / `decode` / `decode_complete` API (plus JWK helpers) while keeping verified decoding tied to an explicit `algorithms` list by default. See [Migration from PyJWT](usage/migration-pyjwt.md) for exception hierarchy changes in **0.2.0**. Release **0.4.0** is beta-quality; see [Versioning](versioning.md) for stability expectations.
OxyJWT is a Python JWT/JWS library with a Rust implementation underneath. It gives Python code a **PyJWT-compatible** `encode` / `decode` / `decode_complete` API (plus JWK helpers) while keeping verified decoding tied to an explicit `algorithms` list by default. See [Migration from PyJWT](usage/migration-pyjwt.md) for exception hierarchy changes in **0.2.0**. Release **0.5.0** is beta-quality; see [Versioning](versioning.md) for stability expectations.

The short version:

Expand Down
2 changes: 1 addition & 1 deletion docs-site/docs/security.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ When you pass a raw `str` / `bytes` HMAC secret (or use `EncodingKey.from_secret

## Compact JWT size limit

OxyJWT rejects compact JWT strings larger than **256 KiB** (same order of magnitude as the default JWKS `max_bytes` cap) with `DecodeError` before base64 or JSON parsing. This applies to verified `decode`, `decode_unverified`, `get_unverified_header`, and `jws_parse_compact`. Legitimate tokens are far smaller; huge inputs are usually denial-of-service attempts.
OxyJWT rejects compact JWT strings larger than **256 KiB** (same order of magnitude as the default JWKS `max_bytes` cap) with `DecodeError` before base64 or JSON parsing. This applies to verified `decode`, `decode_unverified`, `get_unverified_header`, and `jws_parse_compact`. RFC 7797 **`detached_payload`** bytes passed to verified decode are capped at the same limit before copy or JSON parsing. Legitimate tokens are far smaller; huge inputs are usually denial-of-service attempts.

## Treat Unverified Helpers As Inspection Only

Expand Down
2 changes: 1 addition & 1 deletion docs-site/docs/versioning.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Typical **major** (breaking) changes include:

Releases `0.x` are pre-1.0. Minor bumps in `0.x` may include small API adjustments while PyJWT parity hardens. After **1.0.0**, breaking changes are reserved for major versions.

The PyPI `Development Status` classifier tracks maturity (Beta on the `0.4.x` line; **Stable** is planned for `1.0.0`).
The PyPI `Development Status` classifier tracks maturity (Beta on the `0.5.x` line; **Stable** is planned for `1.0.0`).

## Rust crate version

Expand Down
1 change: 1 addition & 0 deletions docs/GITFLOW.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ pytest
gh pr create --base dev --title "feat(jwks): refresh JWKS on unknown kid (#42)" --body "Closes #42"
```
6. After review and green CI, squash-merge or merge commit into `dev`.
Direct pushes to `dev` also run [CI](.github/workflows/ci.yml) (same jobs as PRs).
7. Sync again, then delete the feature branch:
```bash
git fetch --all --prune
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "maturin"

[project]
name = "oxyjwt"
version = "0.4.0"
version = "0.5.0"
description = "A Python JWT library powered by a Rust core."
readme = "README.md"
requires-python = ">=3.10"
Expand Down
2 changes: 1 addition & 1 deletion python/oxyjwt/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""OxyJWT public API (PyJWT-shaped module surface)."""

__version__ = "0.4.0"
__version__ = "0.5.0"

from ._oxyjwt import (
DecodingKey,
Expand Down
Loading
Loading