Standards: WIT -02 WPT -01 Txn-Token -11 SPIFFE-CA -02 Identifiers -03 mTLS -02 Arch -08 DPoP RFC 9449 OID-FED 1.0 Final Standards reference →
Chapter 1 of 12

The Problem

Two workloads on the internet want to talk. But how does Workload B know the request really came from Workload A — and not an attacker?

Workload A cloud-a.example Attacker intercepts! Workload B cloud-b.example ? No identity proof
The core problem: TCP/IP packets carry no inherent identity. Anyone can claim to be anyone. A service receiving a request has zero cryptographic proof of the caller's identity.
Without identity
Any process can forge a request
No way to detect impersonation
Credentials get shared, leaked, stolen
WIMSE's answer
Cryptographic workload identity
Short-lived, unforgeable JWT tokens
Proof-of-possession per request
What you'll learn: How WIMSE uses JWT + X.509 + proof-of-possession to give each workload a verifiable identity — from first principles, powered by real Go crypto compiled to WebAssembly.
Chapter 2 of 12

WIT: Your Identity Card

The Workload Identity Token (WIT) is a signed JWT issued by a trusted Identity Provider. It carries the workload's URI-based identity and a public key for proof-of-possession.

Spec: draft-ietf-wimse-workload-creds-02  |  typ: wit+jwt  |  alg: ES256
Identity Provider idp.cloud-a.example Workload A cloud-a.example Workload B cloud-b.example
Chapter 3 of 12

mTLS: Lock the Channel

Mutual TLS adds a transport-layer identity layer: both sides present X.509 certificates with SPIFFE URI Subject Alternative Names before any data flows.

Spec: draft-ietf-wimse-mutual-tls-02  |  EC P-256, TLS 1.3+, URI SAN
Unauthenticated
TLS Handshake
mTLS Established
Workload A spiffe://cloud-a.../a Workload B spiffe://cloud-b.../b
Chapter 4 of 12

WPT: Prove Each Request

A Workload Proof Token (WPT) is a short-lived, per-request JWT signed with the workload's private key. It binds to the WIT and to the exact target URI — making token theft useless.

Spec: draft-ietf-wimse-wpt-01  |  typ: application/wpt+jwt  |  TTL: 5 minutes
Workload A WIT + priv key Workload B verifies WIT+WPT
Chapter 5 of 12

The Validation Dance

When Workload B receives a request it runs 9 checks before letting it through. Each one protects against a specific attack class.

Workload A sends WIT+WPT Validator 9 checks… WIT + WPT
Chapter 6 of 12

Fine-Grained Authorization

Authentication answers “who are you?” — Authorization answers “what are you allowed to do?” The WIT's sub and trust_domain drive fine-grained access control.

Workload A authenticated Middleware WIT validated Policy Engine ABAC rules Resource api:billing-data
Resource: Action:
Subject PatternResourceActionDecisionReason
Run the check to see policy evaluation
WIMSE + OPA/Cedar/OpenFGA: In production the policy engine is a dedicated service. The WIT's sub claim becomes the principal in the authorization query — no additional identity lookup needed.
Chapter 7 of 12

Selective Disclosure

An SD-WIT uses the SD-JWT standard to let a workload present only the claims a specific service needs — without re-issuance. Hidden claims are replaced with cryptographic hashes.

Format: JWT _sd hash array + Base64URL disclosures after ~ separator
Workload A presents SD-WIT Workload B sees only revealed SD-WIT
Chapter 8 of 12

Token Exchange

Workload A (cloud-a.example) needs to call Workload B (cloud-b.example). Problem: B only trusts its own IdP. A Token Exchange Service bridges the trust domains.

Spec: draft-ietf-wimse-arch-08 — token exchange at trust-domain boundaries
Workload A cloud-a.example has WIT-A Token Exchange trusts both IdPs policy gateway Workload B cloud-b.example accepts WIT-B ① WIT-A ③ WIT-B ② Exchange validates WIT-A, applies trust policy, issues WIT-B signed by cloud-b IdP key IdP-A cloud-a.example IdP-B cloud-b.example
Workload A POSTs WIT-A (iss=cloud-a) to the Token Exchange Service at POST /token/exchange
Exchange validates WIT-A against IdP-A's JWKS, applies cross-domain trust policy, optionally rewrites the subject principal
Exchange issues WIT-B (iss=cloud-b) signed with IdP-B's private key — accepted natively by Workload B's validator
A generates a fresh WPT over WIT-B using its own private key, then sends WIT-B + WPT to Workload B
No federation required
IdP-A and IdP-B don't need to know about each other. Only the Exchange Service needs bilateral trust configuration.
Subject rewriting
spiffe://cloud-a.example/svc/billing → wimse://cloud-b.example/external/billing — mapped by policy, fully auditable.
Multi-cloud in practice: This is how a workload in AWS calls a workload in GCP without a shared IdP or hard-coded credentials. The Exchange Service is the minimal, auditable trust boundary between domains.
Chapter 9 of 12

Attacks & Defenses

What happens when an attacker captures a valid WPT and tries to replay it? WIMSE's jti tracking makes replay attacks impossible — even with a perfectly valid, unexpired stolen token.

Workload A legitimate caller Attacker captured WPT Workload B records jti Workload B jti already seen! WIT+WPT WPT (replay)
Defence complete. From raw TCP with no identity, to cryptographically verifiable, short-lived, replay-protected, fine-grained workload identity. One more chapter shows how OpenID Federation lets entirely separate organisations trust each other's IdPs — without a shared secrets or a pre-arranged static key list.
Chapter 10 of 12

OpenID Federation

Two organisations have their own IdPs and never exchanged a single key out-of-band. OpenID Federation 1.0 lets a shared Trust Anchor publish cryptographically-signed Subordinate Statements that vouch for each IdP, so any relying party can verify the chain at runtime.

Spec: OpenID Federation 1.0 — entity statements, trust chains, trust anchors
Trust Anchor trust-anchor.corp.example signs Subordinate Statements SS (signed by anchor) SS (signed by anchor) IdP-A idp.cloud-a.example Entity Configuration JWT IdP-B idp.cloud-b.example Entity Configuration JWT Federation Resolver verifies trust chains at runtime SS JWT Key Resolved

Step 1 — Build the federation chain

Generate a Trust Anchor key pair, an IdP-B key pair, build a self-signed Entity Configuration for IdP-B, and have the Trust Anchor sign a Subordinate Statement certifying IdP-B's public key.

Step 2 — Verify the trust chain

The Resolver walks the chain: parse the EC to find authority_hints, fetch the Subordinate Statement from the Trust Anchor, verify the SS signature, extract IdP-B's key from the SS, verify the EC was signed by that key.

Step 3 — Federated exchange

A workload presents a WIT signed by IdP-B. The relying party has no static entry for IdP-B — it resolves the key dynamically via the federation resolver, then validates the WIT normally.

Story complete. From raw TCP with no identity — to cryptographically verifiable, short-lived, replay-protected, fine-grained, federated workload identity across independent trust domains. All cryptographic operations ran in real Go code compiled to WebAssembly in your browser.
Chapter 11 of 12

Transaction Tokens

A WIT answers "who is this workload?" — but in a microservice call chain, the receiving service also needs to know "why is this call happening?" Transaction Tokens (txntoken+jwt, draft-ietf-oauth-transaction-tokens-11) capture the full originating context — user identity, authorisation details, and transaction ID — so every hop in a chain can trust the same root decision without re-authenticating the user.

Spec: draft-ietf-oauth-transaction-tokens-11  |  typ: txntoken+jwt  |  immutable across call chain
Gateway entry point Alice's request TTS mints txntoken 5-min TTL Workload B validates WIT+WPT + txntoken Workload C same txntoken unmodified Click Animate to see the flow mint txntoken WIT+WPT+Txn TxnToken
The problem
Workload A authenticates Alice and calls B → C. B and C see only the service WIT — they have no idea Alice triggered this. Per-hop user identity is error-prone and inconsistent.
The Txn-Token solution
At the entry point a Transaction Token Service (TTS) mints a txntoken+jwt that is immutable and forwarded unchanged through the chain. Every service verifies the same token.
User request arrives at the edge workload (gateway). The TTS mints a txntoken+jwt carrying the user's identity, originating IP, entry-point workload SPIFFE ID, and the AS-granted authorisation details.
Gateway forwards the request to Workload B with its WIT+WPT (service auth) plus the Txn-Token in a separate header. The Txn-Token is not replaced — it travels unmodified.
Workload B validates the WIT+WPT for service identity, then validates the Txn-Token for user context. It now knows both who the caller workload is and who the end user is and what they were authorised to do.
B calls C in the same way. C sees the same Txn-Token — full auditability across every hop, signed once by the TTS, verifiable by any service with the TTS public key.

Go — TTS issues & service validates

// pkg/txntoken — TTS mints
tts := txntoken.NewIssuer(
  "https://tts.cloud-a.example",
  ttsKey, 5*time.Minute)

tok, _ := tts.Issue(txntoken.IssueOptions{
  Subject:   "alice@corp.example",
  Audiences: []string{"https://api.…"},
  ReqCtx:    &txntoken.RequestContext{
    ReqIP: "203.0.113.42",
    ReqWL: "spiffe://…/svc/gateway",
  },
})

// Downstream service validates
v := txntoken.NewValidator(
  "https://tts.cloud-a.example", ttsPub)
claims, _ := v.Validate(tok)
// claims.Txn, claims.ReqCtx, claims.AzDetails
WPT binding: A WPT can optionally include a tth claim — base64url(SHA-256(txntoken_compact)) — cryptographically tying the per-request proof to the transaction. The pkg/wpt generator accepts a TxnToken field that sets this automatically.
10 tests in pkg/txntoken/ — happy path, typ header, txn ID propagation, authorisation details, wrong key, expired token, issuer mismatch, missing subject/audience, hash determinism, empty string guard.
Chapter 12 of 12

SPIFFE Client Authentication

Workloads already carry WITs that cryptographically prove their SPIFFE identity. Traditional OAuth client_credentials flows require a client_secret — a credential that must be stored, rotated, and protected. SPIFFE Client Auth (draft-ietf-oauth-spiffe-client-auth-02) eliminates the secret entirely: the WIT itself becomes the client assertion.

Spec: draft-ietf-oauth-spiffe-client-auth-02  |  RFC 7523 JWT Bearer client assertion  |  client_assertion_type: jwt-bearer
IdP / SPIRE issues wit+jwt cnf.jwk bound Workload spiffe://…/billing no client_secret OAuth AS validates WIT sig issues Bearer Resource API Bearer token sub = SPIFFE ID Click Animate to see the flow wit+jwt client_assertion Bearer API call
Standard foundation (RFC 7523)
RFC 7523 defines urn:ietf:params:oauth:client-assertion-type:jwt-bearer — an OAuth client can authenticate with a signed JWT instead of a secret. SPIFFE Client Auth profiles this for WIMSE workloads: the WIT is that signed JWT.
What the AS gains
The AS validates the WIT's ECDSA signature against the IdP's JWKS — trust it already extends for workload auth. No client_id/client_secret pre-registration. The SPIFFE URI in sub serves as the client identifier.
Workload holds its WIT (issued by IdP). It needs an OAuth Bearer token to call a Resource API — but there is no client_secret anywhere.
Workload sends POST /token to the OAuth AS with grant_type=client_credentials, client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer, and client_assertion=<WIT>.
AS fetches the IdP's JWKS, verifies the WIT's ES256 signature and expiry. On success it issues a Bearer access token with sub=spiffe://…/svc/billing — the workload's SPIFFE identity is preserved.
Workload calls the Resource API with the Bearer token. The resource server sees sub = SPIFFE URI. Zero secrets exchanged anywhere in the entire flow.

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
Credential lifecycle solved: WITs have short TTLs — when one expires the workload requests a fresh one from its IdP automatically. No operator involvement, no rotation scripts, no secret scanners, no vault integrations. The AS needs only the IdP's public key, already distributed via /.well-known/jwks.json.
8 tests in pkg/spiffeclientauth/ — happy path, token uniqueness per call, wrong assertion type, empty assertion, tampered WIT, expired WIT, issuer mismatch, scope passthrough.
Story complete. From raw TCP with no identity — to cryptographically verifiable, short-lived, replay-protected, fine-grained, federated workload identity with transaction context and secretless OAuth authentication. All cryptographic operations ran in real Go code compiled to WebAssembly in your browser.