Scenario 1

The Identity Sprawl Problem

Large enterprises running workloads across AWS, Azure, and GCP accumulate siloed, duplicate identities — one per cloud, per service, per team. The result: unauditable access, orphaned credentials, and a constant risk of lateral movement.

SAME BILLING SERVICE — THREE SEPARATE IDENTITIES AWS (us-east-1) billing-svc arn:aws:iam::9876:role/billing payment-svc arn:aws:iam::9876:role/payment audit-svc arn:aws:iam::9876:role/audit Azure (westus2) billing-svc app:billing-svc@tenant-abc payment-svc app:payment-svc@tenant-abc audit-svc app:audit-svc@tenant-abc GCP (us-central1) billing-svc billing@proj.iam.gserviceaccount payment-svc payment@proj.iam.gserviceaccount audit-svc audit@proj.iam.gserviceaccount
The billing service is the same logical workload but it has three different identities, three different access policies, three different audit trails — and no way to answer "which cloud instances of billing-svc called payment-svc last week?" across all three.
Symptoms of identity sprawl
ProblemHow it manifestsBusiness risk
Duplicate credentialsbilling-svc has 3 IAM roles / service principals / SAs3× rotation surface, 3× audit burden
Orphaned identitiesDecommissioned services leave IAM roles that are never cleaned upLateral movement vector; compliance failure
Policy driftAWS policy updated; Azure policy forgotten; GCP policy conflictsUnintended access granted or blocked
Cross-cloud accessbilling-svc (AWS) calls payment-svc (GCP) via long-lived secretsSecret leakage = full compromise
Audit gapsNo unified identity → no unified access log across cloudsSOC 2 / ISO 27001 non-compliance
Root cause: Every cloud hyperscaler provides its own identity primitive (IAM role, service principal, service account). None of them speak to each other. The workload has no portable, verifiable identity that works outside its originating cloud.
Why not just use what's already there?

Every team asks this first. Here's what each existing approach provides — and the specific gap that WIMSE fills.

Existing approach What it gives you What it doesn't solve
OAuth Bearer token
Authorization: Bearer eyJ…
Authorization for an action or scope on one AS Ambient authority — anyone holding the token can use it (no key binding). Stolen token = full access until expiry. Cross-cloud calls require federation setup per AS pair. No per-request, per-URI proof.
mTLS alone (SPIFFE/SPIRE)
X.509 cert, URI SAN
Strong transport-layer identity per TCP connection Certificate identity doesn't survive TLS-terminating load balancers or service meshes. Cert validity is hours or days — no short-lived, per-request proof. Doesn't express what the workload is calling or why.
API keys / shared secrets
X-Api-Key: sk-live-XXXX
Simple, fast service-to-service auth Static and long-lived. Shared across all instances of the service — one compromise = full compromise. No automatic expiry. No workload-specific identity; the key is indistinguishable from a leaked copy.
Cloud IAM roles
AWS IRSA · GCP WIF · Azure MI
Strong cryptographic identity within one cloud Cloud-specific — AWS IRSA tokens aren't accepted by a GCP resource. No portable identity URI that works cross-cloud, on-prem, or at edge. No standard for cross-domain workload auth.
Kubernetes Service Accounts
projected serviceAccountToken
Pod identity within a single cluster Cluster-scoped. All pods in the same Deployment share one SA — no per-workload-instance identity. Tokens aren't accepted outside the cluster they were issued for.
WIMSE adds the missing layer: a portable spiffe://trust-domain/svc/billing URI that works everywhere, a short-lived WIT bound to the workload's EC key pair (stolen token → useless without the key), and a per-request WPT tied to the exact target URI — making replay and forgery cryptographically impossible. Cloud IAM still handles infrastructure bootstrapping; WIMSE handles every application-layer call.
Scenario 2

WIMSE + SPIFFE as a Universal Identity Overlay

WIMSE (Workload Identity in Multi-System Environments) + SPIFFE (Secure Production Identity Framework for Everyone) provides a portable, cryptographic identity layer that sits above cloud-native identity — giving every workload a single canonical identity that works everywhere.

The three-layer identity stack
Application Layer — WIMSE + SPIFFE spiffe://corp.example/svc/billing — portable across ALL clouds. WIT + WPT prove identity per request. OID-FED federates trust domains.
↓ maps to
Cloud Identity — AWS IAM arn:aws:iam::9876:role/billing — bootstraps the workload key only
Cloud Identity — Entra ID billing@tenant-abc — federated to WIMSE IdP via WIF
Cloud Identity — GCP SA billing@proj.iam.gserviceaccount.com — workload identity pool bridge
↓ runs on
Infrastructure — EC2 / AKS / GKE / On-Prem / Edge Physical or virtual compute. WIMSE is infrastructure-agnostic — it only requires a key store and an HTTPS endpoint.
What WIMSE provides
1
Portable SPIFFE URIs

spiffe://corp.example/svc/billing — the same identity string in AWS, Azure, GCP, on-prem, and edge.

2
Short-lived, cryptographic tokens

WITs expire in minutes/hours. No long-lived secrets to rotate or leak.

3
Per-request proof

WPTs bind each API call to a specific target URI and the caller's private key — replay and forgery are cryptographically impossible.

4
Federated trust

OID-FED lets independently-operated IdPs (Okta, Keycloak, Vault, Entra ID) join a common trust fabric without sharing keys.

What stays cloud-native
WIMSE doesn't replace cloud IAM — it builds on top of it. Cloud-native identity is used for bootstrapping the workload's EC P-256 key pair. All application-layer calls use WIMSE after that.
LayerWho owns it
Compute (EC2/Pod/VM)Cloud provider
Bootstrap identity (IRSA/WIF/WIP)Cloud provider
Workload key generationSPIFFE/SPIRE or Vault
WIT issuanceWIMSE IdP (Okta/Keycloak/Vault)
API-level auth (WIT+WPT)WIMSE protocol
Cross-cloud trustOID-FED trust anchor
Interactive — Multi-Cloud Workload Authentication Flow
AWS → GCP
Azure → AWS
GCP → On-Prem
AWS EKS Pod spiffe://cloud-a.corp/svc/billing bootstrap: IRSA Corp WIMSE IdP Keycloak + SPIRE OID-FED trust anchor GCP GKE Pod spiffe://cloud-b.corp/svc/analytics validate: OID-FED WIT request → ← WIT issued WIT + WPT → ← 200 OK
Bootstrap
AWS pod uses IRSA to get initial cloud identity. EC P-256 key pair generated; public key registered with Corp IdP.
WIT Issued
Corp IdP validates IRSA token, issues WIT: sub=spiffe://cloud-a.corp/svc/billing, cnf.jwk=<pub-key>, exp=+1h.
Cross-Cloud Call
AWS pod generates WPT (aud=https://analytics.gcp.corp/api, wth=hash(WIT)). Calls GCP service with Workload-Identity-Token + Workload-Proof-Token headers.
OID-FED Validated
GCP service resolves Corp IdP via OID-FED trust chain. Validates WIT signature + WPT audience binding. Returns 200 OK.
Scenario 3

Integrating Existing IdP Platforms

Most enterprises already have one (or more) of these identity platforms running. WIMSE can be layered on top of each — you keep your existing investment and add portable workload identity on top.

SaaS · Cloud
Okta Workforce Identity / Auth0

Integration Pattern

OAuth 2.0 token exchange + custom claims
Workload authenticates to Okta using its cloud-native credential (IRSA/WIF/Workload Identity Pool).
Okta validates the cloud credential and issues an OAuth access token with custom spiffe_uri and workload_key claims.
A lightweight WIMSE IdP sidecar exchanges the Okta token for a proper WIT (typ: wit+jwt) with correct cnf.jwk.
The workload uses the WIT + per-request WPT for all subsequent service-to-service calls.

Pros & Cons

Pros

  • Existing Okta contract; no new vendor
  • Centralised policy (Okta PAM / OIG)
  • Audit log in one place
  • Auth0 great for multi-tenant SaaS workloads

Cons

  • SaaS dependency — offline = no tokens
  • Custom claims require Okta hooks (paid tier)
  • Latency overhead on token exchange path
  • Rate limits affect high-throughput workloads
Cloud · Microsoft
Azure Entra ID (formerly Azure AD)

Integration Pattern

Workload Identity Federation (WIF) + Managed Identity bridge
Configure Entra ID Workload Identity Federation to trust SPIFFE SVIDs from your SPIRE deployment. Entra ID becomes an OID-FED participant.
Azure Managed Identity or federated identity credential provides the initial bootstrap token for on-prem or multi-cloud workloads.
WIMSE IdP (Keycloak or Vault) validates the Entra ID token via OIDC and issues a WIT with sub=spiffe://corp.example/....
Entra ID's Conditional Access policies govern which workloads may exchange tokens — centralised access control for cross-cloud calls.

Pros & Cons

Pros

  • Native to Azure workloads (AKS, App Service)
  • WIF eliminates secrets for Azure → AWS/GCP flows
  • Conditional Access = zero-trust policy engine
  • Microsoft 365 compliance logs included

Cons

  • Strong Azure lock-in outside Azure boundary
  • WIF setup is complex for multi-hop scenarios
  • Requires Azure subscription even for non-Azure workloads
  • Microsoft Graph API dependency for automation
Open Source · Self-hosted
Keycloak

Integration Pattern

Custom token mapper + OIDC WIT issuance
Deploy Keycloak as the central WIMSE IdP. Create a realm per trust domain (cloud-a.example, cloud-b.example).
Write a Keycloak custom protocol mapper that adds cnf.jwk (workload public key) to the token. This transforms a standard Keycloak JWT into a valid WIT.
Workloads authenticate via the machine-to-machine client credentials flow, presenting their EC P-256 public key in the token request.
Keycloak's realm configuration is shared as an OID-FED Entity Configuration, making it a first-class OID-FED participant under the corporate trust anchor.

Pros & Cons

Pros

  • Fully open source — no vendor lock-in
  • Complete control over token format and claims
  • On-premises deployment for air-gapped environments
  • Rich admin UI; Terraform/Ansible manageable

Cons

  • Self-managed HA cluster — operational burden
  • Custom mapper requires Java development skills
  • No native SLA; support via Red Hat SSO subscription
  • Scaling to thousands of workloads requires tuning
OSS + Enterprise · HashiCorp
HashiCorp Vault

Integration Pattern

Vault as IdP + PKI secrets engine for mTLS + WIMSE sidecar
Use Vault's PKI secrets engine to issue mTLS certificates with SPIFFE URI SANs. Each workload pod/container gets a short-lived cert.
Use Vault's JWT/OIDC auth method to accept the mTLS cert identity and issue a Vault token scoped to the workload's policies.
A WIMSE sidecar (or Vault Agent plugin) exchanges the Vault token for a WIT via the WIMSE IdP endpoint, embedding the workload's public key in cnf.jwk.
Vault's audit log captures every secret access and token exchange — providing the unified identity audit trail that cloud IAM alone cannot.

Pros & Cons

Pros

  • Unified secrets + identity in one platform
  • Rich audit log across all secret accesses
  • Dynamic secrets eliminate static credentials
  • HCP Vault available as managed service

Cons

  • Vault Enterprise needed for DR, replication, FIPS
  • Complex HA setup (Raft/Consul); unsealing operational burden
  • WIMSE WIT issuance requires custom plugin or sidecar
  • Per-secret pricing can surprise at scale
Recommendation: Most enterprises use a combination. Vault for secrets + PKI; Keycloak or Okta as the WIMSE IdP; Entra ID for Azure-native workloads. WIMSE's OID-FED layer lets all three coexist under a single corporate trust anchor.
Interactive — Token Acquisition Flow by Product
Okta / Auth0
Entra ID
Keycloak
Vault
Workload spiffe://corp.example/… Cloud Auth IRSA / WIF / WIP IdP Okta / Auth0 Target Service WIT + WPT auth
Step 1
Step 2
Step 3
Step 4
Scenario 4

Trust Governance with OpenID Federation

OID-FED 1.0 is the trust governance layer that links independently-operated IdPs into a coherent enterprise trust fabric — without requiring point-to-point key exchange between every pair of IdPs.

Corporate OID-FED trust hierarchy
Corporate Trust Anchor trust-anchor.corp.example SS SS SS SS Okta (AWS workloads) idp.aws.corp.example Entra ID (Azure) idp.azure.corp.example Keycloak (GCP / On-Prem) idp.onprem.corp.example Vault (Secrets) idp.vault.corp.example billing-svc spiffe://corp.example/svc/billing payment-svc spiffe://corp.example/svc/payment audit-svc spiffe://corp.example/svc/audit
Key insight: Every workload still has one canonical identity (spiffe://corp.example/svc/billing) regardless of which cloud it runs on or which IdP issued its token. The Trust Anchor is the single source of truth — not any individual IdP.
How trust resolution works at runtime
1
Token arrives at the gateway

The gateway peeks at the iss claim (e.g., idp.azure.corp.example) without verifying the signature yet.

2
Federation resolver kicks in

No static key for this issuer? The resolver fetches the Entity Configuration from https://idp.azure.corp.example/.well-known/openid-federation.

3
Walk the trust chain

EC's authority_hints points to the corporate Trust Anchor. The resolver fetches the Subordinate Statement and verifies it with the pre-configured anchor key.

4
Validate the token normally

The issuer's public key (certified by the SS) is used to verify the WIT signature. If valid, the workload is authenticated.

5
Result is cached

Resolved keys are cached until min(EC.exp, SS.exp). Subsequent requests use the cache; no network round trip needed.

Zero-config expansion: When a new IdP (e.g., a new GCP project) joins, it only needs to register with the Trust Anchor. Every gateway in the estate automatically trusts tokens from the new IdP — without any reconfiguration.
Live OID-FED trust resolution — animated walkthrough
billing-svc spiffe://corp.example/… Gateway validates WIT OID-FED Resolver chain walker IdP (Entra ID) idp.azure.corp.example Trust Anchor trust-anchor.corp.example Click Play to start
① billing-svc presents WIT to Gateway
Gateway receives Workload-Identity-Token header, peeks at iss claim: idp.azure.corp.example — no cached key for this issuer.
② OID-FED Resolver fetches Entity Configuration from IdP
Resolver fetches https://idp.azure.corp.example/.well-known/openid-federation. Entity Configuration JWT contains the IdP's JWKS and authority_hints.
③ authority_hints points to Trust Anchor — fetch Subordinate Statement
Resolver fetches https://trust-anchor.corp.example/federation/fetch?sub=idp.azure.corp.example. Subordinate Statement certifies the IdP's public key, signed by the anchor.
④ Trust chain verified — IdP key certified by anchor
Resolver validates SS signature against the pre-configured Trust Anchor key. IdP JWKS is now trusted. Result cached until min(EC.exp, SS.exp).
⑤ WIT signature verified — 200 OK
Gateway uses the certified IdP public key to verify the WIT signature and expiry. sub=spiffe://corp.example/svc/billing authenticated. Request forwarded.
Scenario 5

Centralised vs Distributed Management

There is no universally correct answer. The right model depends on your organisation's size, regulatory requirements, operational maturity, and tolerance for single points of failure.

Identity management models

Centralised IdP

Pros

  • Single audit log for all workload tokens
  • Consistent policy enforcement — one place to update
  • Easier SOC 2 / ISO 27001 evidence collection
  • Fewer infrastructure components to manage overall
  • SPIFFE SPIRE in central mode fits this model

Cons

  • Single point of failure — IdP outage = no new tokens
  • Network latency for geographically distributed workloads
  • Large blast radius if IdP is compromised
  • Bottleneck for high-frequency token issuance

Distributed IdPs (OID-FED)

Pros

  • Each cloud/region has a local IdP → low latency
  • No single point of failure — one IdP down ≠ whole estate down
  • Teams own their IdP → faster delivery, less bottleneck
  • Smaller blast radius per IdP compromise
  • Cloud-native IdPs (Entra, GCP WIP) used where they shine

Cons

  • More complex: Trust Anchor + per-IdP SS management
  • Policy drift risk if IdPs are not centrally governed
  • Audit logs spread across multiple IdPs → aggregation needed
  • Trust Anchor becomes the new single critical dependency
Decision matrix — which model fits your context?
Situation
Recommendation
Reason
Single cloud, <50 workloads
Centralised
Simplicity wins at this scale. One Keycloak or Okta instance suffices.
Multi-cloud, same org, <200 workloads
Hybrid
Central IdP with regional cache/proxy. OID-FED for cross-cloud validation only.
Multi-cloud, 200+ workloads, SLO > 99.9%
Distributed
Per-cloud IdPs (Entra/Keycloak/Vault) federated via Trust Anchor. Local issuance eliminates cross-cloud dependency.
Air-gapped / regulated (FedRAMP, PCI-DSS)
Centralised
Strict audit and network control requirements favour a single, hardened IdP (Vault Enterprise or on-prem Keycloak).
M&A / acquired subsidiary with own IdP
Distributed
OID-FED is the right tool: add the subsidiary IdP as a subordinate under the parent Trust Anchor. No migration needed.
Multi-tenant SaaS platform
Distributed
Each tenant's workloads get their own IdP (or realm). Trust Anchor governs cross-tenant access with explicit policy.
Animated comparison — token flow patterns

Centralised IdP — single bottleneck

Central IdP Okta / Keycloak billing-svc payment-svc audit-svc Single point of failure

Distributed IdPs — federated via OID-FED

Trust Anchor corp.example IdP AWS Okta IdP GCP Keycloak billing payment audit analytics Local issuance — no cross-cloud dependency
Scenario 6

Secrets Management — Centralised vs Distributed

Workload identity and secrets management are closely related but distinct concerns. WIMSE handles identity (who is this workload?) while secrets management handles access to sensitive data (what secrets may this workload use?).

Two fundamentally different approaches

Centralised Secrets (Vault / AWS Secrets Manager)

Pros

  • Single audit trail for all secret accesses
  • Dynamic secrets — DB credentials auto-rotated per workload
  • Fine-grained policies: who may read which secret
  • Centralised rotation — no per-workload rotation burden

Cons

  • Vault outage = all workloads lose access to secrets
  • Network hop to Vault on every secret access
  • Bootstrapping problem: how does a new workload authenticate to Vault?
  • HA Vault cluster is complex and expensive

WIMSE Distributed Key Approach

Pros

  • No shared secrets — workload generates its own EC P-256 key
  • Private key never leaves the workload's process
  • No bootstrap problem: SPIRE SVIDs or cloud metadata → WIT
  • Compromise is scoped to one workload instance

Cons

  • Application secrets (DB passwords, API keys) still need Vault
  • Key rotation requires workload restart or SVID renewal
  • No central store → harder to audit key creation events
  • Relies on workload runtime integrity (container/VM isolation)
Recommended pattern: WIMSE + Vault together
🔑
Workload Identity Key

EC P-256 key pair generated at startup. Private key in memory only. Public key in WIT cnf.jwk. WIMSE handles this.

🗃️
Application Secrets

DB credentials, API keys, TLS certs — retrieved from Vault at startup using the WIMSE WIT as the Vault auth credential. Vault issues dynamic secrets.

📜
mTLS Certificates

Short-lived X.509 certs with SPIFFE URI SANs issued by Vault PKI. Renewed every 1–8 hours by SPIRE or Vault Agent. Tied to the workload identity key.

1
Bootstrap

Cloud-native credential (IRSA/WIF/GKE WIP) → WIMSE IdP → WIT. No manually managed secrets needed for this step.

2
Authenticate to Vault

Present WIT to Vault's JWT auth endpoint. Vault validates WIT via the WIMSE IdP's JWKS. Returns a Vault token scoped to the workload's policy.

3
Retrieve application secrets

Use the Vault token to fetch dynamic DB credentials, encrypt API keys, or generate mTLS certs from Vault PKI. All accesses are in Vault's audit log.

4
Service-to-service calls

Use WIMSE WIT + per-request WPT for all inter-service communication. No long-lived shared secrets involved.

The net result: Vault audits what secrets were used. WIMSE audits who made each service call. Together they give you the complete picture that neither can provide alone.
Animated — Combined pattern: Bootstrap → Vault → Service calls
Workload startup WIMSE IdP issues WIT Vault dynamic secrets Target Service WIT+WPT auth
Bootstrap
Cloud-native credential (IRSA/WIF) → WIMSE IdP → WIT issued. No manually-managed secrets.
Authenticate to Vault
Present WIT to Vault JWT auth endpoint. Vault validates via WIMSE IdP JWKS → Vault token.
Retrieve Application Secrets
Use Vault token to fetch dynamic DB credentials, mTLS certs (PKI), and API keys. All in Vault audit log.
Service-to-Service Calls
Use WIMSE WIT + per-request WPT for all inter-service calls. No long-lived shared secrets.
Scenario 7

Enterprise Adoption Roadmap

Moving from cloud-native IAM silos to a WIMSE-based identity fabric is a phased journey. Each phase delivers immediate value and reduces risk, making the next phase easier.

Phased rollout
1
Phase 1 — Inventory and SPIFFE naming (weeks 1–4)

Assign spiffe://corp.example/svc/<name> URIs to all workloads. No code changes yet — this is purely a naming exercise. Build the inventory that will drive policy later.

2
Phase 2 — WIMSE IdP deployment (weeks 4–8)

Deploy a central WIMSE IdP (Keycloak or Vault) in a pilot cloud. Workloads in the pilot exchange cloud-native credentials for WITs. mTLS certs from Vault PKI for transport security.

3
Phase 3 — WIT + WPT on service-to-service calls (weeks 8–16)

Instrument the 5–10 highest-value service pairs with WIMSE middleware. This immediately eliminates long-lived shared secrets on the busiest call paths and provides per-request audit.

4
Phase 4 — OID-FED federation (weeks 16–24)

Establish the corporate Trust Anchor. Register existing IdPs (Entra ID, Okta, Keycloak) as subordinates. Cross-cloud service calls can now resolve trust dynamically — no more bilateral key exchange.

5
Phase 5 — Fine-grained authorization (weeks 24–36)

Deploy an authorization layer (OpenFGA or OPA) behind the WIMSE gateway. Replace overly broad IAM policies with subject × resource × action rules driven by SPIFFE URI patterns.

6
Phase 6 — Agentic workloads (ongoing)

As AI agents and service orchestrators proliferate, WIMSE's SPIFFE URI namespace and short-lived cryptographic tokens apply directly: each agent process gets a workload identity, and every action it takes can be attributed to that identity in the audit log. No new protocol needed — WIMSE scales naturally to agentic patterns.

Key success metrics
MetricBefore WIMSEAfter WIMSE
Credential types per workload3–5 (IAM role, SA, secret, cert, token)1 (EC P-256 key pair)
Longest-lived credentialOften permanent (IAM role, static API key)WIT: 1h max; WPT: 5 min
Cross-cloud auth mechanismLong-lived shared secret or OIDC trust per pairOID-FED — dynamic, anchor-governed
Identity audit trailPer-cloud, not unifiedSingle SPIFFE URI in every access log
Time to add new trusted IdPDays (bilateral key exchange)Minutes (Trust Anchor + SS)
M&A identity integrationWeeks of IAM configurationRegister new IdP with Trust Anchor
Try the technical PoC: The Standard PoC demo lets you run all cryptographic operations live in your browser via WebAssembly — WIT issuance, mTLS, WPT proof-of-possession, SD-JWT, token exchange, OID-FED trust chains, and replay attack defence. No server, no installation required.
Scenario 8

Post-Quantum Safety for WIMSE

WIMSE currently relies on EC P-256 (ECDSA) for all token signatures and mTLS certificates. Shor's algorithm running on a cryptographically relevant quantum computer (CRQC) can break elliptic curve discrete logarithm in polynomial time — rendering every WIT, WPT, and mTLS cert forgeable.

Harvest Now, Decrypt Later (HNDL): Adversaries are already capturing encrypted WIMSE traffic and storing it. When a CRQC becomes available (~2030–2035 by most estimates), they can retroactively forge tokens, decrypt sessions, and impersonate any workload whose identity material was observed in transit.
Quantum Computer vs. EC P-256 — Attack Simulation

EC P-256 relies on the Elliptic Curve Discrete Logarithm Problem: given public point Q = k·G, finding private key k requires ~2128 classical operations — computationally infeasible. Shor's algorithm on a CRQC solves ECDLP in O(n³), extracting k in seconds.

EC P-256 Key Q = k·G public: Q known private: k hidden Classical Computer Baby-step Giant-step · ~2¹²⁸ ops infeasible 0% → heat death of universe Quantum CRQC Shor's Algorithm · QFT + period finding · O(n³) polynomial time 0% → seconds on CRQC Result awaiting simulation
① Both computers receive EC public key Q=k·G
Classical and quantum attempt to recover the hidden private key k.
② Classical stalls — Baby-step Giant-step requires ~2¹²⁸ steps
EC P-256 security holds — progress halts well below 1%. Computationally infeasible in any foreseeable classical timeframe.
③ CRQC: Shor's Algorithm extracts private key k
Quantum Fourier Transform finds the discrete logarithm period. Private key k recovered — every past WIT and WPT signature is now forgeable.
Threat timeline
Now (2025–2028) EC P-256 secure. Harvest attacks begin. Prepare now.
Transition (2028–2032) Hybrid EC + ML-DSA dual-sign. Both signature types accepted.
Quantum Era (2032+) EC-only issuers rejected. Pure ML-DSA or SLH-DSA required.
NIST PQC standards (2024 finalists)
Signature

ML-DSA

CRYSTALS-Dilithium. Lattice-based. Primary NIST recommendation for digital signatures.

Public key1312 B (vs 64 B for EC)
Signature2420 B (vs 64 B for EC)
Security128-bit PQ equivalent
WIMSE useWIT signing, WPT signing
Key Exchange

ML-KEM

CRYSTALS-Kyber. Lattice-based key encapsulation. Replaces ECDH in TLS 1.3 key exchange.

Public key1184 B (vs 32 B for ECDH)
Ciphertext1088 B (vs 32 B)
Security128-bit PQ equivalent
WIMSE usemTLS key exchange in TLS 1.3
Hash-Based

SLH-DSA

SPHINCS+. Stateless hash-based signatures. Conservative option; much larger signatures but only relies on hash security.

Public key32 B (small!)
Signature8 KB – 50 KB
Security256-bit PQ equivalent
WIMSE useTrust Anchor signing (long-lived keys)
Three-Era Migration Roadmap — Live Progressive View

Click each era to explore the token structure, pros, cons, and required migration actions for that phase of the quantum transition.

WIT Token Structure
{
  "typ": "wit+jwt",
  "alg": "ES256"
}.{
  "sub": "spiffe://cloud-a/svc/billing",
  "cnf": { "jwk": { "kty":"EC",
    "crv":"P-256","alg":"ES256" } },
  "exp": ..., "jti": "..."
}
mTLS
TLS 1.3
ECDHE + ECDSA
EC P-256 cert
URI SAN: spiffe://...
Pros
Small tokens (~64 B sig). Fast ES256 verification. Universal library support. Battle-tested in production environments.
Cons / Risks
Vulnerable to Shor's algorithm on CRQC. HNDL: today's WIT traffic is collectible for future decryption. Migration window opens ~2028.
Action items now: Inventory all WIT/WPT signing keys and mTLS CA material. Upgrade validators to be alg-agnostic. Avoid hardcoding "alg":"ES256" — use allow-list validation.
WIMSE component impact — what needs to change?
WIMSE ComponentCurrent (EC P-256)PQ-SafeMigration Path
WIT signing ES256 (ECDSA) ML-DSA (alg=ML-DSA-44) Hybrid: both alg in header during transition
WPT signing ES256 (ECDSA) ML-DSA or SLH-DSA Same hybrid approach; WPT key confirmed in WIT cnf.jwk
mTLS certificates EC P-256 X.509 ML-DSA + ML-KEM in TLS 1.3 Dual-cert: server presents both EC and PQ cert
cnf.jwk key type EC {kty:"EC", crv:"P-256"} OKP {kty:"OKP", crv:"ML-DSA-44"} New JWK key type. WIT parser must handle both.
OID-FED entity config EC key in JWKS ML-DSA key in JWKS Publish both EC and ML-DSA keys during transition
Trust Anchor signing EC P-256 SLH-DSA (conservative, high-assurance) Trust Anchors have long-lived keys → prioritise PQ-migration
Hybrid signature approach — transition period

During the EC → PQ transition, WIMSE tokens carry both an EC and a PQ signature. Validators that understand PQ verify the PQ signature; legacy validators fall back to EC. Once PQ is universal, the EC signature is dropped.

📝
Phase 1 — Hybrid Issuance

WIT header: alg:["ES256","ML-DSA-44"]. Token carries both signatures. Works with EC-only and PQ-capable validators.

🔄
Phase 2 — Dual Accept

All validators updated to accept ML-DSA. Issuers prefer ML-DSA but still include EC sig. JWKS exposes both key types.

🔒
Phase 3 — PQ Only

EC-only tokens rejected. alg:ML-DSA-44 mandatory. mTLS requires ML-KEM. Trust Anchor uses SLH-DSA. Migration complete.

Quantum Threat Model — Updated
ThreatVectorClassical RiskQuantum RiskMitigation
T-Q1: CRQC Key Extraction Shor's algorithm extracts EC P-256 private key from public point Q Low — 2¹²⁸ ops Critical — seconds on CRQC Migrate all WIT/WPT keys to ML-DSA-44; deprecate ES256
T-Q2: HNDL — WIT/WPT Tokens Collect WITs/WPTs in transit today; recover sig keys post-CRQC; forge workload identity None High — every workload identity forgeable Deploy hybrid signing from 2028; PQ-only mandatory by 2032
T-Q3: HNDL — mTLS Sessions Record TLS handshakes; ECDH session secrets recoverable via Shor's once CRQC available None High — all past mTLS session keys exposed Deploy ML-KEM-768 (X25519MLKEM768) for TLS key exchange
T-Q4: Trust Anchor Forgery Extract Trust Anchor EC key; issue fake OID-FED entity statements; take over federation Low Critical — entire federation trust graph collapses SLH-DSA for Trust Anchors (highest priority — long-lived keys)
T-Q5: JTI Hash Weakening (Grover) Grover's algorithm halves SHA-256 pre-image resistance from 256 to 128 effective bits Negligible Medium — SHA-256 JTIs weaker but not immediately broken Upgrade JTI generation to SHA-512 or 256-bit cryptographic random
T-Q6: WPT Proof Token Forgery Recover workload private key via Shor's; generate valid WPT for any target URI Low Critical — per-request proof protection bypassed for all historical WPTs Migrate WPT cnf.jwk to OKP/ML-DSA; enforce short proof TTL ≤ 5 min
WIMSE's modular design helps: Because WIMSE separates token issuance (IdP), key confirmation (cnf.jwk), and validation (middleware), each component can be upgraded independently. The SPIFFE URI identity (sub) remains unchanged — only the cryptographic algorithm changes.
Action now: (1) Inventory all trust material (WIT signing keys, mTLS CA, OID-FED Trust Anchor). (2) Prioritise Trust Anchor PQ migration — it's the highest-impact long-lived key. (3) Deploy hybrid-capable validators before the quantum deadline. (4) Monitor NIST and IETF (e.g. draft-ietf-jose-pqc) for finalised JOSE PQ algorithm identifiers.
Scenario 9

Paxos Consensus for Distributed WIMSE

A high-availability WIMSE deployment runs multiple IdP replicas. Without consensus, two replicas might issue conflicting tokens, or a JTI replay might succeed by hitting a replica that hasn't seen the first request. Paxos provides the agreement primitive that makes distributed WIMSE correct.

Why consensus matters — WIMSE pain points
ProblemWithout consensusWith Paxos
Token issuance in HA clusterTwo primaries race → duplicate JTIs, split claimsOnly one leader issues; majority agrees before commit
JTI replay detectionReplay succeeds by hitting a replica that hasn't seen the JTIJTI store is replicated via Raft/Paxos; replay rejected cluster-wide
Trust Anchor key rotationReplicas briefly serve old and new keys — validation failsKey rotation committed atomically; all replicas switch simultaneously
Token revocation propagationRevocation reaches some replicas later — revoked tokens still acceptedRevocation log replicated; all replicas reject revoked token immediately
OID-FED SS cache invalidationStale Subordinate Statements used after expiryCache expiry events propagated consistently to all gateway nodes
Paxos protocol — animated 3-node token issuance

Proposal value = {sub, cnf.jwk, jti, exp}. The proposer (IdP primary) must obtain a quorum (2 of 3) before issuing the token.

Proposer (Primary) idp-0.corp.example Acceptor idp-1.corp.example Acceptor idp-2.corp.example Prepare(n=1) Promise(n=1) Prepare(n=1) Promise(n=1) Accept(n=1,v) Accept(n=1,v) Click ▶ Run Paxos Round to begin
Phase 1a — Prepare(n)
Proposer broadcasts Prepare(ballot=1) to all acceptors. "I want to propose something with ballot number 1."
Phase 1b — Promise(n)
Acceptors respond with Promise: "I won't accept any proposal with ballot < 1. Here's the last value I accepted (if any)."
Phase 2a — Accept(n, v)
Proposer has a quorum. Broadcasts Accept(ballot=1, value={sub, cnf.jwk, jti, exp}). "Please accept this token payload."
Phase 2b — Accepted + Token Issued
Acceptors commit. Quorum reached. Token is issued. All replicas have the same JTI in their replay store.
Practical deployment — Raft over classic Paxos

Most WIMSE deployments use Raft (Multi-Paxos equivalent with simpler leader election) rather than classic Paxos. etcd and CockroachDB both use Raft. A WIMSE HA cluster with Raft looks like:

1
3 or 5 IdP replicas

Odd quorum. Leader elected by Raft. Leader handles all token issuance writes. Followers serve reads (JWKS, health).

2
Replicated JTI store

Every JTI is written to the Raft log before the token is issued. All replicas see the same JTI history → replay detection works across nodes.

3
Key rotation via Raft commit

New signing key committed as a Raft entry with a future activation timestamp. All replicas switch at the same logical time → no split-brain key state.

4
Revocation log

Revoked JTIs written as Raft entries. Gateways subscribe to the revocation feed via SSE or gRPC stream from the leader.

Quorum sizing rule: For a WIMSE IdP cluster with N nodes, you need ⌊N/2⌋+1 nodes to form a quorum. 3-node cluster: 2 nodes needed. 5-node cluster: 3 nodes needed. With 3 nodes, you can tolerate 1 failure; with 5 nodes, you can tolerate 2 — which covers a rolling deploy of a multi-zone cluster.
Consistency vs latency tradeoff: Synchronous Raft commit adds ~1–5ms latency per token issuance (one RTT to quorum). For most WIMSE deployments this is acceptable since tokens are cached and reused for their TTL (typically 1h). For ultra-low-latency cases, use async replication with leader-only issuance and accept a small window of split-brain risk.
Scenario 10

Standards Tracker

This PoC is built on nine implemented IETF and OpenID drafts, plus five additional drafts under active monitoring (see table below). Every implemented standard has an animated demo chapter. A GitHub Actions workflow checks the IETF Datatracker API daily and opens a GitHub issue whenever a new revision is published.

IETF standard lifecycle
Individual Draft
WG Draft
Last Call
IESG Review
Published RFC
Obsoleted
PoC status legend
✓ Implemented — Go code + animated demo
◌ Monitoring — tracked, not yet built
✗ Excluded — out of scope (reason documented)
Production readiness
Only Published RFCs should be used in production. WG Drafts are unstable — breaking changes are expected without notice. This PoC implements WG Drafts for early adoption exploration only.
Tracking cadence
Daily automated check via GitHub Actions. last_known_rev tracks the latest published revision; implemented_rev tracks what this PoC has actually implemented.
Tracked standards — revision status

Last checked: 2026-08-02  ·  standards-baseline.json  ·  standards-tracker.yml

Standard Working Group IETF Status PoC Status Rev Affected files Notes
WIT — Workload Identity Credentials IETF WIMSE WG ● WG Draft ✓ Implemented -02 pkg/wit, internal/idp, internal/workload WIT claims (typ=wit+jwt), cnf.jwk with alg=ES256, SPIFFE URI sub
WPT — Workload Proof Token IETF WIMSE WG ● WG Draft ✓ Implemented -01 pkg/wpt, internal/workload typ=application/wpt+jwt, aud=request URI, wth=SHA-256(WIT), tth=SHA-256(Txn-Token), jti replay protection
Txn-Token — OAuth 2.0 Transaction Tokens IETF OAuth WG ● WG Draft ✓ Implemented -11 pkg/txntoken, pkg/wpt (tth) typ=txntoken+jwt, propagates user identity + authz context; WPT tth binds hop proofs to the transaction
Identifiers — Workload Identifier IETF WIMSE WG ● WG Draft ✓ Implemented -03 pkg/wit, pkg/wpt, internal/idp SPIFFE URI format (spiffe://trust-domain/path), no query / fragment / port allowed
mTLS — Mutual TLS Binding IETF WIMSE WG ● WG Draft ✓ Implemented -02 pkg/keys/mtls.go, internal/workload X.509 URI SAN format, EC P-256 requirement, TLS 1.3 minimum
Arch — WIMSE Architecture IETF WIMSE WG ● WG Draft ✓ Implemented -08 internal/exchange Token exchange patterns at trust-domain boundaries, HTTP header names (Workload-Identity-Token)
OID-FED — OpenID Federation 1.0 OpenID Foundation ● Published (OIDF) ✓ Implemented 1.0-41 pkg/federation, internal/exchange Entity Configuration JWT, Subordinate Statement, authority_hints, trust chain resolution, min(EC.exp,SS.exp) caching
SPIFFE Client Auth — JWT-bearer OAuth assertion IETF OAuth WG ● WG Draft ✓ Implemented -02 pkg/spiffeclientauth WIT as client_assertion (urn:…:jwt-bearer); AS validates WIT sig, returns Bearer token with sub=SPIFFE ID
Identity Chaining — JWT Authorization Grant IETF OAuth WG ● WG Draft ✓ Implemented -17 pkg/identitychaining typ=jwt-authz-grant; cross-trust-domain SPIFFE ID propagation; aud=target AS, jti replay, 5-min TTL
JWT-BCP — JWT Best Current Practices IETF OAuth WG ● Active BCP ✓ Implemented bis All validator files (pkg/wpt, pkg/sdwit, pkg/txntoken, pkg/identitychaining) BCP compliance audit 2026-08-02: WithValidMethods(ES256), WithExpirationRequired, WithIssuedAt, typ check — mitigates alg confusion, alg:none, missing-exp. Commit: e6ff6a2
HTTP-Sig — WIMSE HTTP Signature Auth IETF WIMSE WG ● WG Draft ◌ Monitoring pkg/wpt (future — if WG adopts) RFC 9421 HTTP Message Signatures as alternative to WPT; would require pkg/httpsig if adopted as primary by the WG
CrossOrgDelegation — Cross-Org Workload Delegation IETF WIMSE WG ◌ Individual draft ◌ Monitoring pkg/identitychaining, internal/exchange Formalises cross-org delegation requirements; informs Phase 4 token exchange policy. No breaking change to current implementation.
SD-JWT VC — Selective Disclosure VC IETF OAuth WG ● WG Draft ✗ Excluded -17 N/A — pkg/sdwit uses base SD-JWT VC layer (vct, status lists, Issuer-Holder-Verifier) designed for human credentials. M2M selective disclosure already handled by pkg/sdwit using base SD-JWT. VC layer adds no value for workload auth.
PropagationContext — Multi-Hop Identity Context IETF OAuth WG ◌ Individual draft ◌ Monitoring pkg/txntoken (future extension) Structured identity propagation context for OAuth call chains; complementary to Txn-Token; tracked for Phase 4 multi-hop identity enrichment
Automated tracking — how it works
Daily cron — 09:00 UTC
GitHub Actions runs scripts/check_standards.py every morning. The script queries the IETF Datatracker REST API for each tracked draft and compares the returned rev field against standards-baseline.json.
Revision detected → baseline committed
When a new revision appears, last_known_rev is updated and committed with [skip ci] to avoid triggering further runs. A developer sets implemented_rev manually after updating the Go code to match.
GitHub issue created automatically
A standards-update labelled issue is opened listing the new revision, the PoC impact note, affected files, and an action checklist: read the IETF diff, check for breaking claim or header changes, update the implementation, and bump implemented_rev.
Manual-only standards: OpenID Federation 1.0 has no IETF Datatracker API. The tracker skips it from the automated check and flags it for manual review in any generated issue.
Scenario 11

Attack Scenarios & Validation Failures

WIMSE enforces multiple independent validation layers. Each layer catches a distinct class of attack. This scenario walks through six real attack vectors an adversary might attempt against a WIMSE-protected service — and shows exactly which check stops them.

Validation pipeline — what the middleware verifies (in order)
#CheckSpec clauseCatches
1typ = wit+jwtworkload-creds §4.1Tokens of wrong type (e.g. a WPT presented as a WIT)
2ECDSA signature valid against IdP public keyRFC 7515 §5.2Tampered payload or forged tokens
3iss matches expected issuerRFC 7519 §4.1.1Tokens from an untrusted or attacker-controlled IdP
4exp not in the pastRFC 7519 §4.1.4Captured and replayed stale tokens
5cnf.jwk parsed — workload public key extractedworkload-creds §4.2WITs with missing or malformed confirmation key
6WPT typ = application/wpt+jwtwpt §3.1Wrong proof token type
7WPT signature valid against cnf.jwk from WITwpt §4Proof signed with a different key than claimed in WIT
8WPT aud = request URI (scheme + host + path)wpt §3.2Proof captured for endpoint A, replayed against endpoint B
9wth = base64url(SHA-256(WIT wire bytes))wpt §3.3WIT and WPT issued by different parties (mix-and-match)
10WPT jti not in replay storewpt §4.2 + RFC 9449Exact replay of a previously accepted request
Attack scenarios — what happens when an attacker tries
Attack 1 — Tampered WIT
What: Attacker intercepts a WIT and flips bits in the payload (e.g. changes sub to a higher-privilege identity).
Caught by: Check #2 — ECDSA signature verification fails. The IdP's public key won't validate a modified payload.
→ 401 Unauthorizedjwt: signature is invalid
Attack 2 — Expired WIT replayed
What: Attacker captures a valid WIT during normal traffic and saves it. Attempts to reuse it hours later (after the 1 h TTL).
Caught by: Check #4 — exp claim is in the past. Short-lived tokens (1 h default) limit the window an attacker can exploit a stolen credential.
→ 401 Unauthorizedjwt: token is expired
Attack 3 — Wrong-issuer token
What: Attacker obtains a valid WIT from idp.cloud-b.example and presents it to Workload B in cloud-a.example, hoping the validator won't check the issuer.
Caught by: Check #3 — the validator is seeded with the trusted IdP's public key. A token signed by a different IdP will fail signature verification (different key) or issuer check.
→ 401 Unauthorized — issuer mismatch
Attack 4 — WPT audience mismatch
What: Attacker captures a WPT bound to https://api.example/orders and replays it against https://api.example/payments (a higher-privilege endpoint).
Caught by: Check #8 — aud in the WPT is compared against the actual request URI. Mismatch → reject. Each WPT is single-use for one specific endpoint.
→ 401 Unauthorizedaud mismatch
Attack 5 — WPT replay (same endpoint)
What: Attacker captures a complete valid request (WIT + WPT headers) and replays the identical HTTP request to the same endpoint within the proof's TTL window.
Caught by: Check #10 — the jti UUID is stored on first acceptance. The second request presents the same jti, which is already in the replay store → rejected immediately.
→ 401 Unauthorizedjti already seen
Attack 6 — Key substitution (cnf mismatch)
What: Attacker steals a victim's WIT (valid, signed by IdP) but doesn't have the victim's private key. They sign a WPT with their own private key and present both headers.
Caught by: Check #7 — WPT signature is verified against the public key extracted from cnf.jwk in the WIT. Attacker's key ≠ victim's cnf.jwk → signature invalid.
→ 401 UnauthorizedWPT: signature invalid
Defense-in-depth — why each layer matters
Defence layerWithout itWith it
Short-lived WIT (1 h TTL)Stolen token valid indefinitelyAttack window capped at 1 hour
Per-request WPT (aud binding)One captured proof works on any endpointEach proof is endpoint-scoped and single-use
WPT jti replay storePassive attacker can replay valid requestsExact replay rejected in O(1) via in-memory set
wth binding (WIT ↔ WPT)Attacker can mix-and-match WIT from A with WPT from BWIT and WPT must form a cryptographically linked pair
mTLS (cert URI SAN = WIT sub)Token presented over plain TLS — MITM can forward stolen headersTransport identity must match token identity — MITM impossible without the private key
What WIMSE does not prevent on its own: authorisation (what the authenticated workload is allowed to do) — see Scenario 12 for fine-grained authorisation. Compromised workload private keys must be detected via cert rotation / SPIRE TTL.
Scenario 12

Fine-Grained Authorization

WIMSE answers who is this workload? It does not answer is this workload allowed to do this specific thing? Fine-grained authorization (FGA) is the complementary layer that closes that gap — and it is the layer that most enterprises deprioritise because it is hard to integrate. This scenario makes the case for it and shows how it composes naturally with WIMSE.

Two separate concerns — authentication vs authorization
WIMSE — Authentication (who)
Answers: "Is this really spiffe://cloud-a.example/svc/billing calling?"

Mechanism: WIT signed by trusted IdP + WPT proof of key possession + mTLS cert URI SAN binding.

Output: a verified sub claim that downstream services can trust unconditionally.
FGA — Authorization (what)
Answers: "Is billing-svc allowed to write to invoice:monthly-2026-07?"

Mechanism: relationship graph (Zanzibar / OpenFGA) or attribute policy (OPA / Cedar) evaluated at request time.

Output: allow / deny decision — no code change needed when policy changes.
billing-svc SPIFFE SVID + WIT WIMSE Gateway ① verify WIT sig + exp ② verify WPT aud + wth + jti → extract verified sub FGA Engine ③ check(user, action, resource) OpenFGA / OPA / Cedar → allow / deny invoice-api protected resource WIT+WPT mTLS sub + action allow → forward Layer 1 · WIMSE AuthN Layer 2 · FGA AuthZ
OpenFGA example — relationship model for invoice-api

OpenFGA uses a type system and tuples (user, relation, object). The WIMSE sub becomes the FGA user. Policy changes happen in the store — no code deployment needed.

Type system (schema)
type workload relations define reader: [workload] define writer: [workload] define admin: [workload] define can_read: reader define can_write: writer define can_delete: admin
Policy tuples (data)
(billing-svc, writer, invoice:*) (audit-svc, reader, invoice:*) (ops-svc, admin, invoice:*) # billing-svc can write invoices # billing-svc CANNOT delete them # only ops-svc can delete
Check results at runtime
✓ allow billing-svc write invoice:*
✓ allow audit-svc read invoice:*
✗ deny  billing-svc delete invoice:*
✗ deny  audit-svc write invoice:*
✓ allow ops-svc delete invoice:*
Why enterprises deprioritise FGA — and why that is a mistake
The objectionThe reality
"It adds an extra network hop per request" OpenFGA, OPA, and Cedar all support local evaluation via a sidecar or embedded SDK. A policy decision is typically <1 ms. Caching with short TTLs (seconds) further reduces overhead without losing freshness.
"Every service needs to be integrated" The FGA check lives in the gateway or a shared middleware — exactly where WIMSE validation already lives. Services receive the allow/deny verdict as a header or context value. No per-service code change needed.
"The schema is hard to keep current" WIMSE's verified sub maps directly to an FGA user without translation. Relations and tuples change in the policy store — no code deployment. WIMSE makes FGA easier because identity is cryptographically verified, not asserted.
"We already have RBAC in our app" Hardcoded RBAC in application code cannot express attribute- or relationship-based policies without deployments. It also cannot be audited or changed without a code review cycle — slowing down incident response by hours or days.
"We'll add it later" The cost of retrofitting FGA grows with the number of services. Adding FGA early — when WIMSE is being deployed — means one integration point, not N. "Later" usually means never.
FGA engine options — which to choose
OpenFGA
Google Zanzibar model. Relationship-based. Strong consistency. CNCF project. Best for hierarchical resources (org → team → document).
Go / Kubernetes native
OPA / Rego
Policy-as-code. Attribute-based. Embeds as a sidecar (Envoy ext_authz). Best for complex conditional logic involving request attributes.
CNCF graduated
Cedar (AWS)
Formally verified policy language. Typed. Very fast evaluation. Best when safety proofs matter (e.g. financial or healthcare workloads).
Open source, AWS-backed
SpiceDB / Authzed
Zanzibar model with a managed cloud option. Drop-in alternative to OpenFGA for teams that want SaaS operations with Kubernetes-native deployment.
Open source + SaaS
Recommended pattern for this PoC: WIMSE validates the token in gateway middleware → extracts sub → forwards X-Workload-Sub header to upstream → upstream calls OpenFGA /stores/{id}/check with user=sub, relation=action, object=resource. Policy tuples managed via GitOps — any team can propose a PR to grant or revoke access. No code deployment required.
Scenario 13

SPIFFE Client Authentication

Workloads already hold a WIT (wit+jwt) that proves their SPIFFE identity. Traditional OAuth client authentication requires a client_id and client_secret — a rotation burden that fails in ephemeral workload environments. SPIFFE Client Auth (draft-ietf-oauth-spiffe-client-auth-02) lets the workload use its existing WIT as a JWT bearer client assertion, authenticating to any OAuth 2.0 AS with zero pre-shared secrets.

OAuth 2.0 client_credentials — WIT as client assertion
IdP / SPIRE idp.cloud-a.example issues wit+jwt cnf.jwk bound to workload Workload spiffe://cloud-a.example /svc/billing OAuth AS POST /token validates WIT signature issues Bearer token Resource API Bearer token in header sub = SPIFFE ID ① wit+jwt ② client_assertion ③ Bearer access_token ④ API call

No pre-shared client secret anywhere in the flow. The AS validates the WIT's ECDSA signature against the IdP's public key — the same trust it already extends to workload authentication.

OAuth request (POST /token)
# application/x-www-form-urlencoded
grant_type=client_credentials
client_assertion_type=urn:ietf:params:oauth:
  client-assertion-type:jwt-bearer
client_assertion=<compact WIT JWT>
scope=read:orders write:payments

# Response
{
  "access_token": "<opaque 32-byte bearer>",
  "token_type":   "Bearer",
  "expires_in":   3600,
  "sub":          "spiffe://cloud-a.example/svc/billing"
}
Go — AS-side Authenticator
// pkg/spiffeclientauth
auth := spiffeclientauth.NewAuthenticator(
  "https://as.cloud-a.example",
  wit.NewValidator(
    "https://idp.cloud-a.example",
    idpPub,
  ),
  time.Hour,
)

tok, err := auth.Authenticate(
  spiffeclientauth.AuthRequest{
    ClientAssertion:     witToken,
    ClientAssertionType: spiffeclientauth.ClientAssertionType,
    Scope: "read:orders",
  },
)
// tok.Sub = "spiffe://cloud-a.example/svc/billing"
// tok.Token = opaque Bearer string
Why SPIFFE Client Auth over client_secret?
Concern client_secret approach SPIFFE Client Auth
Secret storageSecret must be stored in vault or env var; risk of leak or misconfigurationNo secret — the WIT is the credential; IdP's JWKS endpoint provides the verification key
RotationManual rotation required; zero-downtime rollover is complexWIT has short TTL; expiry is automatic rotation with no operator involvement
Identity in tokenAccess token carries only client_id — no workload identityAccess token's sub = SPIFFE URI — resource server gets workload identity for free
Per-workload credentialsOne secret per registration — manual, audit-unfriendly, scale bottleneckEvery workload holds its own SVID; no registration needed at the AS

8 tests in pkg/spiffeclientauth/spiffeclientauth_test.go: happy path, token uniqueness, wrong assertion type, empty assertion, tampered WIT, expired WIT, issuer mismatch, scope passthrough.

Live SPIFFE Client Auth flow — animated walkthrough
IdP / SPIRE issues wit+jwt Workload spiffe://…/svc/billing OAuth AS validates WIT sig Resource API Bearer token Click Play to start
① IdP issues WIT to workload
IdP/SPIRE issues a wit+jwt bound to the workload's public key via cnf.jwk. The workload holds this as its identity credential — no client_secret needed.
② Workload sends WIT as client_assertion
POST /token with grant_type=client_credentials, client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer, and client_assertion=<WIT>.
③ AS validates WIT → issues Bearer token
AS fetches IdP's JWKS, verifies the WIT's ECDSA signature and expiry. On success, issues a Bearer access token with sub=spiffe://…/svc/billing.
④ Workload calls Resource API with Bearer token
Standard OAuth Bearer in Authorization header. Resource server sees sub = SPIFFE URI — full workload identity, zero secrets exchanged anywhere in the flow.
Scenario 14

Identity Chaining

A workload in Cloud A needs to call an API in Cloud B. Cloud B's AS won't accept Cloud A's WIT — different trust domains, different signing keys. Identity Chaining (draft-ietf-oauth-identity-chaining-17) bridges this gap: Cloud A's AS issues a short-lived JWT Authorization Grant addressed to Cloud B's token endpoint. Cloud B validates it with only Cloud A's public key — no pre-configured secrets between the two AS instances.

Cross-trust-domain workload identity propagation
CLOUD A (trust domain: cloud-a.example) CLOUD B (trust domain: cloud-b.example) Workload A spiffe://cloud-a.example /svc/billing AS — Cloud A validates wit+jwt issues jwt-authz-grant signed: domainA key aud = Cloud B endpoint AS — Cloud B validates grant iss, aud, exp, sub extracts SPIFFE ID issues Cloud B token API — Cloud B protected endpoint sub = SPIFFE ID ① wit+jwt ② jwt-authz-grant ③ jwt-authz-grant (cross-domain) ④ Cloud B token ⑤ API call

Cloud B only needs Cloud A's public signing key — typically distributed via Cloud A's JWKS endpoint or a mutual trust agreement. The workload's SPIFFE ID flows through the grant's sub claim, giving Cloud B full auditability.

JWT Authorization Grant structure
// Header
{
  "alg": "ES256",
  "typ": "jwt-authz-grant"
}

// Payload
{
  "iss": "https://as.cloud-a.example",
  "sub": "spiffe://cloud-a.example/svc/billing",
  "aud": ["https://as.cloud-b.example/token"],
  "iat": 1753649000,
  "exp": 1753649300,  // 5-min TTL
  "jti": "kY2mNpXx…"  // replay prevention
}
Go — pkg/identitychaining
// Domain A: issue grant
gi := identitychaining.NewGrantIssuer(
  "https://as.cloud-a.example",
  domainAKey, witValidator,
  5*time.Minute,
)
grant, _ := gi.Issue(
  witToken,
  "https://as.cloud-b.example/token",
)

// Domain B: validate grant
gv := identitychaining.NewGrantValidator(
  "https://as.cloud-a.example",
  domainAPub,
)
claims, _ := gv.Validate(
  grant,
  "https://as.cloud-b.example/token",
)
// claims.Subject = "spiffe://cloud-a.example/svc/billing"
Why Identity Chaining?
Concern Without Identity Chaining With Identity Chaining
Cross-domain authWorkload can't authenticate to domain B — different trust roots, no shared secretDomain A's AS issues a grant; domain B validates with domain A's public key only
Identity preservationWorkload must acquire domain B credentials with a separate static identitySPIFFE ID from domain A preserved in grant's sub — full audit trail across boundaries
Infrastructure dependencyRequires federation agreement or a common token exchange serviceGrant is self-contained — domain B needs no live connection to domain A after initial key exchange
Replay safetyA static cross-domain token could be replayed indefinitelyjti uniqueness + 5-minute TTL — intercepted grants expire quickly and cannot be replayed

10 tests in pkg/identitychaining/identitychaining_test.go: happy path, typ header, audience mismatch, wrong signing key, expired grant, issuer mismatch, invalid WIT, missing inputs, end-to-end cross-domain flow.

Live Identity Chaining flow — animated walkthrough
CLOUD A CLOUD B Workload A spiffe://cloud-a/… AS — Cloud A issues jwt-authz-grant AS — Cloud B validates grant API — Cloud B protected endpoint Click Play to start
① Workload A presents WIT to AS-A
Workload sends its wit+jwt to Cloud A's Authorization Server to prove its SPIFFE identity. AS-A validates signature and expiry.
② AS-A issues JWT Authorization Grant
AS-A signs a jwt-authz-grant with iss=as.cloud-a.example, sub=spiffe://cloud-a.example/svc/billing, aud=https://as.cloud-b.example/token, 5-min TTL, random jti.
③ Grant crosses trust-domain boundary → AS-B
Workload A presents the grant to Cloud B's token endpoint. AS-B validates iss, aud, exp, and jti uniqueness using only Cloud A's public key — no pre-shared secrets.
④ AS-B issues Cloud B domain token
AS-B extracts the SPIFFE ID from the grant's sub claim and issues a Cloud B token. Full audit trail — Workload A's identity is preserved across the boundary.
⑤ Workload A calls Cloud B API
The Cloud B API receives a domain-native token, verifies it, and recognises the caller's SPIFFE ID. Zero cross-domain secrets; zero pre-registration at AS-B.
Scenario 15

WIMSE vs Existing Standards

WIMSE does not replace OAuth 2.0, OIDC, or TLS — it composes with them. This scenario maps six common enterprise workload identity flows to the standards teams reach for today, identifies the precise gap each leaves at scale, and shows which WIMSE primitive closes it.

Standards applicability map — six core workload identity flows

Each row is a real flow. Current standard is what teams already have. WIMSE primitive is the composable layer that closes the gap — without replacing the existing investment.

Flow Current standard Gap at scale WIMSE primitive Advantage
Workload-to-workload auth
billing-svc → payment-svc
RFC 8705 mTLS + RFC 6750 Bearer
or shared client_secret
mTLS proves cert identity, not application identity; Bearer tokens are ambient — stolen token = full access until expiry; no per-request URI binding WIT (typ: wit+jwt) + WPT (typ: application/wpt+jwt)
draft-ietf-wimse-workload-creds / draft-ietf-wimse-wpt
Application-layer identity bound to workload's EC key; per-request proof locked to exact target URI — stolen WPT is worthless outside its intended call
OAuth client authentication
workload authenticates to token endpoint
RFC 6749 §2.3 client_secret
or RFC 7523 private_key_jwt
+ client registration at every AS
client_secret is a long-lived shared secret — single compromise affects all workload instances; private_key_jwt requires static key management and pre-registration at each AS SPIFFE Client Auth
draft-ietf-oauth-spiffe-client-auth
WIT is the credential — AS validates against SPIFFE trust domain JWKS; zero pre-registration, zero shared secrets anywhere in the flow
Cross-domain token exchange
cloud-a workload calls cloud-b API
RFC 8693 generic token exchange
or bilateral OIDC federation
+ pre-shared trust per AS pair
RFC 8693 exchanges tokens without defining portable workload identity format; bilateral OIDC requires pre-registration between every pair of AS instances — O(N²) config at scale Identity Chaining
draft-ietf-oauth-identity-chaining (RFC 7523 + RFC 8693 profile)
SPIFFE ID preserved in jwt-authz-grant sub; domain B needs only domain A's public key — N domains need N configs, not N² bilateral agreements
Multi-hop audit trail
user → gateway → svc-A → svc-B
Bearer token forwarding
or unsigned Forwarded / X-Forwarded-For headers
Forwarded Bearer is ambient — any intermediate can forge context; headers are unsigned and trivially spoofable; no cryptographic binding between request hops Txn-Token + WPT tth claim
draft-ietf-oauth-transaction-tokens
tth in every WPT is SHA-256 of the originating Txn-Token; forged intermediate hops are detectable; entire chain is cryptographically auditable
Identity federation at scale
IdP-A trusts IdP-B across orgs
SAML 2.0 metadata federation
or OIDC point-to-point Discovery
per-IdP bilateral registration
SAML is human-centric and operationally heavyweight for machine identities; OIDC Discovery requires bilateral pre-registration for every IdP pair — does not scale to dynamic multi-cloud topologies OID-FED 1.0
openid-federation-1_0 — Entity Configuration + trust chain resolution
Each IdP publishes one Entity Configuration; trust established by path traversal through an authority hierarchy — no bilateral pre-registration; dynamic trust at runtime
Selective credential disclosure
workload shares only needed claims
Full JWT (all claims visible to every relay)
or opaque token + introspection endpoint
RFC 7662
Full JWT leaks every claim (trust domain, environment, role) to every relay in the chain; opaque introspection requires an online round-trip to the token endpoint per request SD-WIT
typ: wit+sd-jwt (base SD-JWT, not SD-JWT VC)
Each relay receives only disclosed claims; no introspection endpoint required; fully offline validation; cryptographic proof of what was not disclosed
① WIT + WPT vs mTLS + Bearer — why application-layer identity matters
mTLS + Bearer token (today)
  • mTLS cert identity doesn't survive TLS-terminating load balancers or service mesh sidecars — the application never sees who connected
  • Bearer tokens have ambient authority — any process holding the token can use it; there is no cryptographic link between the token and the calling process
  • Stolen Bearer token is fully functional until expiry — which may be hours or days
  • No standard way to prove which URI was called — SSRF attacks can use a valid token against unintended endpoints
WIT + WPT (WIMSE)
  • WIT carries the workload's SPIFFE URI (spiffe://trust-domain/svc/billing) in sub, cryptographically signed by the IdP — portable across every layer
  • WPT is signed by the workload's own EC key (proved via cnf.jwk in WIT) — stolen WPT is useless without the private key
  • WPT aud = exact target URI; wth = SHA-256 of the WIT — SSRF and token reuse across endpoints are structurally impossible
  • WPT TTL: 5 minutes; jti replay store prevents double-submission within the window
RFC baseline: WIT profiles RFC 7519 (JWT) with typ=wit+jwt; WPT profiles RFC 7519 with typ=application/wpt+jwt and adds wth (analogous to DPoP's ath in RFC 9449). Both are composed on top of existing OAuth/JWT infrastructure — no new wire protocol.
② SPIFFE Client Auth vs client_secret / private_key_jwt — eliminating shared secrets from OAuth flows
client_secret / private_key_jwt (today)
  • client_secret is a shared secret — stored in env vars or vaults; all workload instances share one secret; single leak compromises all instances
  • private_key_jwt (RFC 7523) is stronger but requires out-of-band client registration at every AS — static key management at each endpoint
  • Both require pre-coordination between the workload operator and AS operator before the first token request
  • Key rotation requires coordinated update at AS and all workload instances
SPIFFE Client Auth (WIMSE)
  • WIT is sent as client_assertion with client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer — a standard RFC 7523 profile
  • AS validates the WIT signature against the SPIFFE trust domain's JWKS endpoint — no pre-registration required at the AS
  • Each workload instance has its own EC key pair; compromise of one instance does not affect others
  • Zero shared secrets in the entire flow — IdP issues short-lived WITs, workload rotates keys at SPIRE TTL intervals automatically
RFC baseline: SPIFFE Client Auth is a strict profile of RFC 7523 §2.2 (JWT Bearer client assertion) — it reuses the existing OAuth token endpoint, content-type, and grant_type. AS operators add one configuration: trust the SPIFFE trust domain's JWKS URL. No protocol changes required.
③ Identity Chaining + Txn-Token vs RFC 8693 + header forwarding
RFC 8693 + Bearer forwarding (today)
  • RFC 8693 token exchange is generic — it defines the exchange protocol but not the identity format; the resulting token may not preserve the caller's workload identity across domains
  • Forwarding the original Bearer token to downstream services grants those services the full authority of the original token — no scope reduction
  • Caller identity in multi-hop chains relies on unsigned headers (X-Caller-ID), which any hop can spoof
  • No cryptographic proof that hop N called hop N+1 — the audit trail is hearsay
Identity Chaining + Txn-Token (WIMSE)
  • Identity Chaining (profiles RFC 7523 + RFC 8693): jwt-authz-grant carries SPIFFE ID in sub; domain B validates with domain A's public key only — SPIFFE ID is preserved end-to-end
  • Txn-Token binds the entire call chain to the originating user context — propagated as a compact signed JWT (typ: txntoken+jwt)
  • WPT tth = SHA-256(Txn-Token): every workload-to-workload hop proves it is part of the same originating transaction — forged intermediate hops are detectable
  • 5-minute TTL + jti uniqueness on every token in the chain
RFC baseline: Identity Chaining profiles RFC 7523 §2.1 (JWT Bearer authorization grant) and RFC 8693 §2.1 (token exchange request). Txn-Token adds a new JWT type but flows over standard Authorization headers. Both compose cleanly with existing AS infrastructure.
④ OID-FED 1.0 vs SAML 2.0 federation and OIDC bilateral registration
SAML 2.0 / OIDC point-to-point (today)
  • SAML 2.0 metadata federation (InCommon, eduGAIN) is designed for human identity and SSO — not workload machine identity; metadata XML format is heavyweight and operationally complex
  • OIDC Discovery-based federation requires bilateral pre-registration: IdP-A registers with IdP-B and vice versa — O(N²) configuration for N IdPs
  • Adding a new IdP to the mesh requires updating every existing peer — operational bottleneck and risk of misconfiguration
  • Trust changes (key rotation, policy updates) require propagating to all bilateral peers
OID-FED 1.0 (WIMSE)
  • Each IdP publishes one self-signed Entity Configuration JWT at /.well-known/openid-federation — one config, all trust
  • Trust is established by traversing the authority hierarchy: leaf → intermediate → Trust Anchor; new IdP joins by registering with one intermediate — O(N) total configurations
  • Trust chain cached at min(EC.exp, SS.exp) — typically 24h; stale chains refresh automatically without manual intervention
  • Key rotation: IdP rotates its EC key pair; the new key appears in its Entity Configuration within the next cache TTL — no bilateral notification required
RFC baseline: OID-FED 1.0 is built entirely on JWT (RFC 7519) and JOSE (RFC 7515/7517). Entity Configurations are signed JWTs; Subordinate Statements are signed JWTs; trust chain resolution is pure public-key cryptography. No new wire format — any JOSE-capable implementation can participate.
The WIMSE thesis in one sentence: Every WIMSE primitive is a precisely-scoped composable layer that closes one specific gap in existing standards — WIT+WPT for ambient token and application-layer identity, SPIFFE Client Auth for shared-secret elimination, Identity Chaining + Txn-Token for cross-domain auditability, OID-FED for federated trust at O(N) cost, SD-WIT for privacy-preserving M2M disclosure — without replacing the OAuth, OIDC, JWT, or TLS infrastructure already deployed.