How to Enable Post-Quantum (ML-DSA) JWT Verification¶
This guide covers enabling ML-DSA-65 and ML-DSA-87 post-quantum signature verification in pico-client-auth.
Prerequisites¶
- Python 3.11+
liboqsC library installed on the systemliboqs-pythonPython bindings
Step 1: Install the PQC Extra¶
This installs liboqs-python, which provides Python bindings for the Open Quantum Safe liboqs C library.
Note:
liboqs-pythonrequires theliboqsC library to be installed on the system. On systems where it's not available via package manager, build it from source (see Docker Setup below).
Step 2: Configure Accepted Algorithms¶
Add ML-DSA algorithms to accepted_algorithms in your configuration:
# application.yaml
auth_client:
issuer: https://auth.example.com
audience: my-api
accepted_algorithms:
- RS256
- ML-DSA-65
Supported algorithms:
| Algorithm | NIST Level | Key Size | Signature Size |
|---|---|---|---|
ML-DSA-65 | Level 3 (minimum recommended) | 1,952 bytes | 3,309 bytes |
ML-DSA-87 | Level 5 (highest) | 2,592 bytes | 4,627 bytes |
Step 3: Serve ML-DSA Keys via JWKS¶
Your auth server's JWKS endpoint must serve ML-DSA public keys using the AKP key type (per draft-ietf-cose-dilithium):
{
"keys": [
{
"kty": "RSA",
"kid": "rsa-key-1",
"alg": "RS256",
"n": "...",
"e": "AQAB"
},
{
"kty": "AKP",
"kid": "pqc-key-1",
"alg": "ML-DSA-65",
"pub": "<base64url-encoded raw public key bytes>"
}
]
}
How It Works¶
When a JWT arrives, TokenValidator reads the alg header and dispatches:
- RS256 tokens are verified via python-jose (existing path)
- ML-DSA-65 / ML-DSA-87 tokens are verified via
pqc_jwtusing liboqs - Tokens with algorithms not in
accepted_algorithmsare rejected withTokenInvalidError
The dispatch is transparent — SecurityContext, @requires_role, @requires_group, and all other features work identically regardless of algorithm.
Graceful Degradation¶
If liboqs-python is not installed but an ML-DSA token arrives, AuthConfigurationError is raised with a clear message:
liboqs-python is required for ML-DSA verification.
Install it with: pip install pico-client-auth[pqc]
RS256 tokens continue to work without liboqs.
Docker Setup¶
For environments where liboqs is not available via package manager, build from source:
FROM python:3.14-slim
RUN apt-get update && apt-get install -y --no-install-recommends \
cmake gcc g++ git ninja-build && \
rm -rf /var/lib/apt/lists/*
RUN git clone --depth=1 https://github.com/open-quantum-safe/liboqs /tmp/liboqs && \
cmake -S /tmp/liboqs -B /tmp/liboqs/build \
-GNinja -DBUILD_SHARED_LIBS=ON -DOQS_BUILD_ONLY_LIB=ON && \
cmake --build /tmp/liboqs/build --parallel $(nproc) && \
cmake --install /tmp/liboqs/build && \
ldconfig && rm -rf /tmp/liboqs
RUN pip install pico-client-auth[pqc]
Testing PQC Tokens¶
PQC test fixtures are provided in conftest.py (skip automatically when liboqs is not installed):
import pytest
def test_mldsa65_token(mldsa65_keypair, make_pqc_token):
oqs = pytest.importorskip("oqs")
public_key, secret_key = mldsa65_keypair
token = make_pqc_token(secret_key, algorithm="ML-DSA-65")
# Use token in your test...
Run PQC tests via Docker:
Or via tox (requires liboqs installed locally):
Migration Strategy¶
A typical migration path from RS256 to ML-DSA:
- Phase 1: Add
ML-DSA-65toaccepted_algorithmsalongsideRS256 - Phase 2: Auth server issues ML-DSA tokens for new clients, RS256 for existing
- Phase 3: All clients migrated — remove
RS256fromaccepted_algorithms