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:

StandardAlgorithmUse CaseSignature Size
FIPS 203ML-KEM (CRYSTALS-Kyber)Key encapsulation / TLS key exchangeN/A (KEM)
FIPS 204ML-DSA (CRYSTALS-Dilithium)Digital signatures: JWT, SAML, certificates~2-3 KB (Level 2)
FIPS 205SLH-DSA (SPHINCS+)Digital signatures, stateless hash-based8-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:

  1. Hybrid PQC TLS (do first — protects against HNDL immediately)
  2. CA certificate upgrade (intermediate and root CAs to ML-DSA)
  3. IdP signing key migration (SAML IdP certificates, JWT signing keys)
  4. Client credential migration (mTLS client certs for service-to-service OAuth)
  5. 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+:

gflroobnasstblsseillnn--ddddeei*ffd:aap4uu_4llf3ttr--osbbnsiitlnneddnc--drcotuprt/vieeotsncs/Xh2sa5sp5lr1-o9mxKiyyn/b-ievdrep7r.6p8TeDLmrSavf1t.030:X25519:P-256

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

APdrmoKKKKAPiveeeelrniyyyygidssooCettAPrroroolaiinrristtsSeeashyoe:swm:ltP:o:etar2iosmdM0npsl:L0gtw--Rs/od(De:krsyS(aedaoAhly:-u-imcsr4gl(i4hSoygkeeaonertkuiyt/rnotipgprhnqp-aagcaksMns-sesLksyw-RewoDSyorSAKsrdAe/d)-kym)6esl5y-dftsooParr-mokhaveikiygedsheteirortsresd.eebcfAcuadfrudkilsttpy)r)oviderjava-keystore

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:

  1. Generate hybrid certificate with ML-DSA as the primary algorithm
  2. Add new certificate to IdP metadata alongside the existing RSA cert (dual-key operation)
  3. Update SP metadata on each relying party to trust both certificates
  4. Monitor SP assertion validation for 30 days
  5. 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:

ProviderPQC TLSML-DSA JWT SigningTimeline
Auth0Partial (Cloudflare hybrid)Not supported2027-2028 roadmap
OktaPartial (Fastly edge)Not supported2027-2028 roadmap
Microsoft Entra IDX25519Kyber768 (preview)Not supportedFY2027
Ping IdentityAvailable in PingFederate 12.2+ML-DSA-44 availableNow
Keycloak 26+Via OQS providerML-DSA-44 via Bouncy CastleNow

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

ItemActionPriority
TLS to hybrid PQCDeploy X25519+Kyber768 on IdP endpoints🔴 Immediate
Inventory RSA/ECC usageopenssl s_client scan all IdP endpoints🔴 Immediate
CA certificate roadmapContact CA vendor for ML-DSA issuance timeline🟡 2026-2027
JWT signing migrationImplement ML-DSA-44 key provider🟡 2027
SAML certificate upgradeDual-key operation → PQC-only🟡 2027-2028
Client library auditVerify JWT/SAML libs support ML-DSA (BouncyCastle 2.x, python-cryptography 43+)🟡 2027
Vendor PQC roadmapsContractually require PQC support dates from Auth0/Okta/Ping🟡 2026
Documentation updateUpdate key rotation runbooks for ML-DSA procedures🟢 2028
PQC auditExternal 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);

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.