What leaks
A platform stores your secrets as environment variables. One breach of that store exposes the keys of every customer at once.
When a hosting platform is breached, every customer rotates every secret it stored. The root cause is the same each time: a stored key is a copy, and copies leak. OIDC workload identity federation replaces the copy with a short-lived, signed proof of who the workload is. This page shows how it works, what changes in a breach, and introduces an open-source issuer for Cloudflare Workers.
A static credential grants access to whoever holds it. To use it, you copy it to every place that needs it. Each copy is a full credential with no expiry, and the cloud cannot tell them apart.
A platform stores your secrets as environment variables. One breach of that store exposes the keys of every customer at once.
Find every key the platform held, rotate it at each provider, update every other copy, then redeploy every service that reads it.
The attacker can use a key the moment it is read. Rotation starts only when you learn of the breach, often days later.
The workload holds no cloud key. It asks a trusted issuer for a signed statement of who it is. The cloud verifies the signature against the issuer’s public keys and hands out temporary credentials. Hover a step to follow it in the diagram.
Over a private service binding. Its identity (sub) and allowed audiences (aud) were fixed at deploy time, so the request cannot change who it is.
RS256, signed with a private key kept in the issuer’s Secrets Store. Claims: iss, sub, aud, an expiry (300 s by default, 1 hour at most) and a unique id.
AssumeRoleWithWebIdentity at AWS, a token exchange at GCP, Azure or Vault. The token travels once, in a POST body.
It verifies the signature with the issuer’s public keys from /.well-known/jwks.json, cached for up to an hour. It checks iss against the registered provider, exp against the clock, and sub and aud against the role’s trust policy.
Scoped to one role, valid for the session length the role allows: 15 minutes in this example, up to 12 hours at AWS. Nothing long-lived was ever stored.
A JSON Web Token: three base64url parts joined by dots. The first two are plain JSON that anyone can read. The third proves the issuer wrote them.
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImsyMDI2MTAyMjBhMWIifQ.eyJpc3MiOiJodHRwczovL29pZGMuZXhhbXBsZS5jb20iLCJzdWIiOiJiaWxsaW5nLXdvcmtlciIsImF1ZCI6InN0cy5hbWF6b25hd3MuY29tIiwiaWF0IjoxNzkyNDYwODAwLCJuYmYiOjE3OTI0NjA4MDAsImV4cCI6MTc5MjQ2MTEwMCwianRpIjoiNWQ3ZTk4ZjEtNmM0YS00ZTJiLWIwYWYtMmI0YzM4YzAxZTU4In0.QmVjYXVzZSB0aGUgaXNzdWVyIHNpZ25lZCBpdCwgdGhlIGNsb3VkIGNhbiB0cnVzdCBpdC4
The role trusts one issuer, one subject, one audience. A valid token for any other workload assumes nothing. This is the AWS form; GCP, Azure and Vault express the same three pins.
In a platform breach with stored keys, the attacker walks away with working credentials. With federation, the same read yields nothing that works tomorrow.
Bar length is illustrative, not to scale. A static key has no window: it works until someone finds and rotates it.
| Static key | OIDC federation | |
|---|---|---|
| What the workload stores | A long-lived secret | Nothing. It asks at runtime. |
| Lifetime of what leaks | Until someone rotates it | Token: 5 min by default. Cloud credentials: typically 15 min to 1 h. |
| Scope of what leaks | Every permission of the key, from anywhere | One identity, one audience, one role |
| Copies | Laptop, CI, platform, backups | None. The private key stays in the issuer. |
| Rotation | Manual, every copy, under pressure | Issuer keys rotate at the issuer, by hand or on a cron. Workloads do not change. |
| Platform breach | Attacker may hold working keys | No stored cloud key to take. Intercepted tokens and credentials expire within the hour. Protect the issuer signing key. |
| Audit | A key looks the same from any caller | Every exchange is logged with its sub |
Every one of these mints an identity token for a running job, and AWS, GCP, Azure, Vault, Claude and OpenAI accept it. Cloudflare Workers has no built-in identity token. Teams on Workers fall back to stored keys.
Open source · MIT
An OIDC workload-identity issuer that deploys into your own Cloudflare account. Zero runtime dependencies in the signer, a guided CLI for setup and key rotation, and provider guides for AWS, GCP, Azure, Vault, the Claude API and OpenAI.
A consumer Worker declares sub and aud in its wrangler.jsonc, as props on the service binding. Only a deployer can set them. The signer reads ctx.props. No request parameter can change who you are.
The signing endpoint is an RPC entrypoint reachable only over a service binding inside your account. The public surface is two GET routes: discovery and JWKS.
Manual mode: keys are generated on your machine, uploaded over stdin, rotated with Add → Promote → Remove in the CLI. Auto mode: a Durable Object generates keys inside Cloudflare and rotates on a cron. Private keys never land in Durable Object storage.
Zero runtime dependencies in the signer. no_nodejs_compat, disable_ctx_exports, an exact JWK member allow-list, non-extractable keys, a TTL ceiling, 156 tests and a written threat model.
Manual is the default and needs no Cloudflare API token. Auto rotates on its own in exchange for one scoped token.
| Manual (default) | Auto (opt-in) | |
|---|---|---|
| Who generates keys | You, on your machine, in memory | A Durable Object, inside Cloudflare |
| Where keys travel | stdin to wrangler, then Secrets Store | Never leave Cloudflare |
| Rotation | Add, Promote, Remove in the CLI | A cron, on a schedule you set |
| Cloudflare API token | None | Required: Secrets Store Edit, this account only |
| Long-lived credential in the system | None | The API token. A leak means denial of service, not forgery. |
Same standard, different job. Login OIDC proves who a person is to an app. Workload identity federation proves who a program is to a cloud provider. There is no user, no browser, no consent screen: a workload gets a signed token and exchanges it for cloud credentials.
We run document APIs on Cloudflare Workers and wanted the workload identity GitHub Actions already has. The issuer is the result. If you ship on Workers, deploy it and stop storing cloud keys.