A June 22, 2026 U.S. executive order mandates all federal agencies and their vendors complete migration to NIST post-quantum cryptographic standards by December 31, 2030. If your identity infrastructure handles government workloads — or if competitors start advertising PQC compliance — you need a concrete migration plan now. This guide covers the specific algorithms, migration sequence, and platform-specific steps for OAuth, JWT, SAML, and TLS in identity systems.
Why Identity Infrastructure Is a Priority Target
Identity systems are the highest-risk category for post-quantum attacks for two reasons:
Long-lived credentials. TLS certificates, SAML signing certificates, and OAuth client secrets often have multi-year lifetimes. A certificate issued today with RSA-2048 will still be in production when quantum computers become capable — and adversaries are harvesting encrypted traffic now to decrypt it retroactively (“harvest now, decrypt later”).
High-value targets. Compromising an OAuth authorization server, SAML IdP, or certificate authority yields persistent access to every downstream service. This makes identity infrastructure more valuable to harvest than application data.
The immediate threat is not “quantum computers can break RSA today.” The threat is: traffic captured today will be decryptable within 5-10 years. For OAuth tokens and SAML assertions with long-lived session data, that window matters.
NIST Post-Quantum Standards: What to Use
NIST finalized three standards in August 2024 that are relevant for identity:
| Standard | Algorithm | Use Case | Signature Size |
|---|---|---|---|
| FIPS 203 | ML-KEM (CRYSTALS-Kyber) | Key encapsulation / TLS key exchange | N/A (KEM) |
| FIPS 204 | ML-DSA (CRYSTALS-Dilithium) | Digital signatures: JWT, SAML, certificates | ~2-3 KB (Level 2) |
| FIPS 205 | SLH-DSA (SPHINCS+) | Digital signatures, stateless hash-based | 8-49 KB |
For identity systems:
- JWT and SAML signing → ML-DSA (FIPS 204). Use Level 2 (ML-DSA-44) for performance; Level 3 (ML-DSA-65) for high-security environments.
- TLS key exchange → ML-KEM (FIPS 203). Deploy as hybrid with X25519 during migration.
- Avoid SLH-DSA for high-frequency operations — 8-49 KB signatures will degrade JWT Bearer token performance significantly. Reserve it for certificate root signing where frequency is low.
Migration Sequence: TLS First, Signatures Second
Migrate in this order:
- Hybrid PQC TLS (do first — protects against HNDL immediately)
- CA certificate upgrade (intermediate and root CAs to ML-DSA)
- IdP signing key migration (SAML IdP certificates, JWT signing keys)
- Client credential migration (mTLS client certs for service-to-service OAuth)
- Token format updates (algorithm identifiers in JWT headers)
Phase 1: Hybrid Post-Quantum TLS
Deploy hybrid cipher suites that combine classical ECDH with ML-KEM. This maintains compatibility with clients that don’t support PQC while protecting against HNDL.
Nginx with OQS Provider:
# Install Open Quantum Safe provider for OpenSSL 3.3+
apt-get install liboqs-dev
# Or build from: https://github.com/open-quantum-safe/liboqs
# nginx.conf server block
server {
listen 443 ssl;
ssl_certificate /etc/ssl/hybrid-cert.pem;
ssl_certificate_key /etc/ssl/hybrid-key.pem;
# Hybrid PQC: prefer X25519Kyber768, fall back to classical
ssl_ecdh_curve X25519Kyber768:X25519:prime256v1;
ssl_protocols TLSv1.3;
ssl_ciphers TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256;
}
HAProxy 2.9+:
Verify hybrid TLS is active:
openssl s_client -connect your-idp.example.com:443 \
-groups X25519Kyber768Draft00:X25519 2>&1 | grep "Server Temp Key"
# Expected: Server Temp Key: X25519Kyber768, 1184 bits
Phase 2: Keycloak Post-Quantum JWT Signing
Keycloak 26+ supports custom key providers via the KeyProvider SPI, which allows plugging in ML-DSA signing keys alongside existing RSA/EC keys.
Step 1: Add Bouncy Castle FIPS provider
# Download bc-fips-2.0.0.jar and bctls-fips-2.0.19.jar
# from https://www.bouncycastle.org/fips-java/
cp bc-fips-2.0.0.jar $KEYCLOAK_HOME/providers/
cp bctls-fips-2.0.19.jar $KEYCLOAK_HOME/providers/
Step 2: Register the ML-DSA key in Admin Console
Step 3: Generate the ML-DSA keystore
keytool -genkeypair \
-alias ml-dsa-signing-key \
-keyalg ML-DSA-44 \
-keystore /opt/keycloak/pqc-keys/ml-dsa-keystore.bcfks \
-storetype BCFKS \
-provider org.bouncycastle.jcajce.provider.BouncyCastleFipsProvider \
-providerpath /opt/keycloak/providers/bc-fips-2.0.0.jar \
-dname "CN=Keycloak PQC Signing Key, O=Corp, C=US" \
-validity 1095
Step 4: Verify JWT algorithm in issued tokens
# Get a token and decode the header
curl -s -X POST https://keycloak.example.com/realms/master/protocol/openid-connect/token \
-d "client_id=test-client&client_secret=secret&grant_type=client_credentials" \
| jq -r '.access_token' \
| cut -d. -f1 \
| base64 -d 2>/dev/null \
| jq .
# Expected: { "alg": "ML-DSA-44", "kid": "...", "typ": "JWT" }
During the transition period, Keycloak will issue tokens with ML-DSA while keeping RSA-256 as a fallback for clients that don’t yet support the new algorithm. Clients validate using the kid header to fetch the correct public key from the JWKS endpoint.
Phase 3: SAML IdP Certificate Migration
SAML signing certificates use RSA or EC. The migration path:
- Generate hybrid certificate with ML-DSA as the primary algorithm
- Add new certificate to IdP metadata alongside the existing RSA cert (dual-key operation)
- Update SP metadata on each relying party to trust both certificates
- Monitor SP assertion validation for 30 days
- Remove the RSA certificate once all SPs have updated
# Generate ML-DSA SAML signing certificate using OpenSSL 3.3+ with OQS
openssl req -x509 -newkey ml-dsa-44 \
-keyout saml-signing-key-pqc.pem \
-out saml-signing-cert-pqc.pem \
-days 1095 \
-subj "/CN=SAML IdP PQC Signing/O=Corp/C=US" \
-nodes
# Verify the certificate algorithm
openssl x509 -in saml-signing-cert-pqc.pem -text -noout | grep "Signature Algorithm"
# Expected: Signature Algorithm: ML-DSA-44
For ForgeRock AM, upload the new PQC certificate under:
Admin Console → Realms → Federation → SAML 2.0 → Signing and Encryption → Add New Signing Certificate
Phase 4: Auth0 and Okta — Current State
As of June 2026, managed identity providers have limited PQC support:
| Provider | PQC TLS | ML-DSA JWT Signing | Timeline |
|---|---|---|---|
| Auth0 | Partial (Cloudflare hybrid) | Not supported | 2027-2028 roadmap |
| Okta | Partial (Fastly edge) | Not supported | 2027-2028 roadmap |
| Microsoft Entra ID | X25519Kyber768 (preview) | Not supported | FY2027 |
| Ping Identity | Available in PingFederate 12.2+ | ML-DSA-44 available | Now |
| Keycloak 26+ | Via OQS provider | ML-DSA-44 via Bouncy Castle | Now |
For Auth0/Okta deployments: Apply PQC at the infrastructure layer:
# Apply post-quantum TLS at your API gateway (Kong, Nginx, Envoy)
# Even if Auth0/Okta don't support ML-DSA JWT signing yet,
# securing your Auth0 Management API calls and callback flows
# with hybrid PQC TLS protects against HNDL.
# Envoy filter chain example
transport_socket:
name: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
common_tls_context:
tls_params:
tls_minimum_protocol_version: TLSv1_3
ecdh_curves:
- X25519Kyber768Draft00
- X25519
Hybrid Migration: Running Old and New Simultaneously
During the 2-4 year migration window, you’ll run classical and PQC algorithms in parallel. Key principles:
Dual-key JWKS endpoint — Publish both RSA-256 and ML-DSA-44 public keys. Clients use the kid header to select the right key. New clients can be updated to prefer ML-DSA; legacy clients fall back to RSA.
Downgrade detection — Log which kid values are being used in token validation. If ML-DSA tokens are being validated, the client supports PQC. Track the ratio to measure migration progress.
# Track algorithm usage in your token validation middleware
def validate_token(token: str) -> dict:
header = jwt.get_unverified_header(token)
alg = header.get("alg", "unknown")
# Emit metric for algorithm usage tracking
metrics.increment("jwt.validation", tags={
"algorithm": alg,
"is_pqc": alg.startswith("ML-")
})
# Validate using appropriate key
jwks = fetch_jwks(header["kid"])
return jwt.decode(token, jwks, algorithms=[alg])
Certificate dual-signing — Some PKI vendors (DigiCert, Entrust) now offer dual-algorithm certificates that include both an RSA and ML-DSA public key. This eliminates the dual-key complexity but requires client support for parsing dual-cert structures.
Compliance Checklist for the 2030 Deadline
| Item | Action | Priority |
|---|---|---|
| TLS to hybrid PQC | Deploy X25519+Kyber768 on IdP endpoints | 🔴 Immediate |
| Inventory RSA/ECC usage | openssl s_client scan all IdP endpoints | 🔴 Immediate |
| CA certificate roadmap | Contact CA vendor for ML-DSA issuance timeline | 🟡 2026-2027 |
| JWT signing migration | Implement ML-DSA-44 key provider | 🟡 2027 |
| SAML certificate upgrade | Dual-key operation → PQC-only | 🟡 2027-2028 |
| Client library audit | Verify JWT/SAML libs support ML-DSA (BouncyCastle 2.x, python-cryptography 43+) | 🟡 2027 |
| Vendor PQC roadmaps | Contractually require PQC support dates from Auth0/Okta/Ping | 🟡 2026 |
| Documentation update | Update key rotation runbooks for ML-DSA procedures | 🟢 2028 |
| PQC audit | External validation of PQC implementation | 🟢 2029-2030 |
Python and Java Library Support
Python (cryptography ≥ 43.0):
from cryptography.hazmat.primitives.asymmetric.ml_dsa import MLDSAPrivateKey, MLDSAPublicKey
from cryptography.hazmat.primitives.asymmetric import ml_dsa
# Generate ML-DSA-44 signing key
private_key = ml_dsa.generate_private_key(ml_dsa.MLDSAParameterSet.ML_DSA_44)
public_key = private_key.public_key()
# Sign a JWT payload manually
message = b"header.payload"
signature = private_key.sign(message)
Java (BouncyCastle 2.0+ FIPS):
import org.bouncycastle.pqc.jcajce.spec.DilithiumParameterSpec;
import java.security.KeyPairGenerator;
KeyPairGenerator kpg = KeyPairGenerator.getInstance("ML-DSA", "BCFIPS");
kpg.initialize(DilithiumParameterSpec.dilithium2); // ML-DSA-44 equivalent
KeyPair keyPair = kpg.generateKeyPair();
Node.js: No native ML-DSA support yet in Node.js crypto. Use @noble/post-quantum (0.2+) for pure-JS ML-DSA implementation:
import { ml_dsa44 } from '@noble/post-quantum/ml-dsa';
const { secretKey, publicKey } = ml_dsa44.keygen();
const signature = ml_dsa44.sign(secretKey, message);
Cross-Links and Further Reading
For context on the broader identity security landscape, see our post-quantum IAM for AI agents overview. For securing service-to-service OAuth flows (where PQC mTLS is most critical), see mTLS Certificate Authentication for Microservices in Kubernetes. For managing token lifecycle during the migration period, see OAuth 2.0 Security Best Practices.
The 2030 deadline is 3.5 years away — enough time to plan, but not enough to delay starting. Begin with the TLS hybrid migration (Phase 1) this year: it’s the highest-impact, lowest-disruption change and immediately protects against harvest-now-decrypt-later attacks.

