Problem
The api-proxy sidecar supports OIDC authentication (AWF_AUTH_TYPE=github-oidc) for AWS Bedrock, GCP Vertex AI, and Azure OpenAI, but not for the Anthropic API itself. Workloads that want to authenticate to Anthropic via short-lived Workload Identity Federation tokens (sk-ant-oat01-..., bound to a Console service account) instead of long-lived static API keys (sk-ant-api03-...) currently have no path through gh-aw.
This blocks the natural cost-attribution + key-governance posture for organisations that want one Anthropic service account per product/workflow rather than a single shared key.
Context
A minimal working POC that bypasses the api-proxy entirely (plain GitHub Actions YAML, Anthropic Python SDK doing the exchange directly) is at:
https://gist.github.com/benissimo/340e7cfb5fdd102c5cd8d39cf91bcc32
The exchange completes in ~3 seconds end-to-end and produces a successful audit event in the Claude Console — proving the federation rule + service-account + exchange chain works against the existing Anthropic API tier (no Enterprise contract required). The remaining gap is purely on the gh-aw / api-proxy side.
Proposed solution
Mirror the AWS / GCP pattern. The change is small and self-contained:
1. New file: containers/api-proxy/anthropic-oidc-token-provider.js
Extends BaseOidcTokenProvider. _doRefresh() does:
mintGitHubOidcToken({audience: 'https://api.anthropic.com'}) (using the existing github-oidc.js helper)
POST https://api.anthropic.com/v1/oauth/token with:
{
"grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer",
"assertion": "<github-oidc-jwt>",
"federation_rule_id": "fdrl_...",
"organization_id": "<uuid>",
"service_account_id": "svac_...",
"workspace_id": "wrkspc_..."
}
- Cache the returned
access_token (sk-ant-oat01-...) and expires_in.
Refresh, retry, shutdown — all inherited from BaseOidcTokenProvider.
2. Edit containers/api-proxy/providers/anthropic.js
Add the OIDC config-parsing block, matching the structure already in providers/openai.js (lines ~70-130). Read from env:
| Env var |
Purpose |
AWF_AUTH_TYPE |
github-oidc to enable |
AWF_AUTH_PROVIDER |
anthropic (selects this branch) |
AWF_AUTH_ANTHROPIC_FEDERATION_RULE_ID |
required |
AWF_AUTH_ANTHROPIC_ORGANIZATION_ID |
required |
AWF_AUTH_ANTHROPIC_SERVICE_ACCOUNT_ID |
required |
AWF_AUTH_ANTHROPIC_WORKSPACE_ID |
optional (only needed if rule covers multiple workspaces) |
AWF_AUTH_OIDC_AUDIENCE |
optional, defaults to https://api.anthropic.com |
Modify getAuthHeaders(req) so that when the OIDC provider is configured, it returns Authorization: Bearer <oat-token-from-provider> (instead of the current x-api-key: <static-key>). Expose getOidcProvider() so server.js's startup latch initializes it.
The existing static-key path stays untouched — selection is by AWF_AUTH_TYPE presence.
3. Tests
Modeled on aws-oidc-token-provider.test.js — JWT mint, exchange POST, cache, refresh schedule, error paths.
4. (Out of scope for this repo, but related)
The githubnext/gh-aw frontmatter schema would need to expose this — likely as engine.auth.type: anthropic-wif plus the rule/org/SA/workspace fields — and compile them down to the AWF_AUTH_* env vars the api-proxy reads. Happy to file a parallel issue there once the api-proxy contract is settled here.
Why this is the right shape
- No
server.js changes. The adapter abstraction handles this entirely via the existing getOidcProvider() hook.
- Refresh problem already solved by
BaseOidcTokenProvider (REFRESH_FACTOR=0.75, auto-schedule, retry-on-failure). Inherited free.
- Env-var forwarding already in place.
awf --env-all forwards ACTIONS_ID_TOKEN_REQUEST_URL and ACTIONS_ID_TOKEN_REQUEST_TOKEN to the api-proxy container; the AWS/GCP providers in providers/openai.js already consume them this way.
- Credential-isolation property preserved. The real
sk-ant-oat01-... never enters the agent container — same property the static-key path provides today.
Problem
The api-proxy sidecar supports OIDC authentication (
AWF_AUTH_TYPE=github-oidc) for AWS Bedrock, GCP Vertex AI, and Azure OpenAI, but not for the Anthropic API itself. Workloads that want to authenticate to Anthropic via short-lived Workload Identity Federation tokens (sk-ant-oat01-..., bound to a Console service account) instead of long-lived static API keys (sk-ant-api03-...) currently have no path through gh-aw.This blocks the natural cost-attribution + key-governance posture for organisations that want one Anthropic service account per product/workflow rather than a single shared key.
Context
engine.auth+BaseOidcTokenProviderfor Azure OpenAI BYOK.aws-oidc-token-provider.jsandgcp-oidc-token-provider.jsfollowed the same pattern.urn:ietf:params:oauth:grant-type:jwt-bearerflow.A minimal working POC that bypasses the api-proxy entirely (plain GitHub Actions YAML, Anthropic Python SDK doing the exchange directly) is at:
https://gist.github.com/benissimo/340e7cfb5fdd102c5cd8d39cf91bcc32
The exchange completes in ~3 seconds end-to-end and produces a successful audit event in the Claude Console — proving the federation rule + service-account + exchange chain works against the existing Anthropic API tier (no Enterprise contract required). The remaining gap is purely on the gh-aw / api-proxy side.
Proposed solution
Mirror the AWS / GCP pattern. The change is small and self-contained:
1. New file:
containers/api-proxy/anthropic-oidc-token-provider.jsExtends
BaseOidcTokenProvider._doRefresh()does:mintGitHubOidcToken({audience: 'https://api.anthropic.com'})(using the existinggithub-oidc.jshelper)POST https://api.anthropic.com/v1/oauth/tokenwith:{ "grant_type": "urn:ietf:params:oauth:grant-type:jwt-bearer", "assertion": "<github-oidc-jwt>", "federation_rule_id": "fdrl_...", "organization_id": "<uuid>", "service_account_id": "svac_...", "workspace_id": "wrkspc_..." }access_token(sk-ant-oat01-...) andexpires_in.Refresh, retry, shutdown — all inherited from
BaseOidcTokenProvider.2. Edit
containers/api-proxy/providers/anthropic.jsAdd the OIDC config-parsing block, matching the structure already in
providers/openai.js(lines ~70-130). Read from env:AWF_AUTH_TYPEgithub-oidcto enableAWF_AUTH_PROVIDERanthropic(selects this branch)AWF_AUTH_ANTHROPIC_FEDERATION_RULE_IDAWF_AUTH_ANTHROPIC_ORGANIZATION_IDAWF_AUTH_ANTHROPIC_SERVICE_ACCOUNT_IDAWF_AUTH_ANTHROPIC_WORKSPACE_IDAWF_AUTH_OIDC_AUDIENCEhttps://api.anthropic.comModify
getAuthHeaders(req)so that when the OIDC provider is configured, it returnsAuthorization: Bearer <oat-token-from-provider>(instead of the currentx-api-key: <static-key>). ExposegetOidcProvider()soserver.js's startup latch initializes it.The existing static-key path stays untouched — selection is by
AWF_AUTH_TYPEpresence.3. Tests
Modeled on
aws-oidc-token-provider.test.js— JWT mint, exchange POST, cache, refresh schedule, error paths.4. (Out of scope for this repo, but related)
The
githubnext/gh-awfrontmatter schema would need to expose this — likely asengine.auth.type: anthropic-wifplus the rule/org/SA/workspace fields — and compile them down to theAWF_AUTH_*env vars the api-proxy reads. Happy to file a parallel issue there once the api-proxy contract is settled here.Why this is the right shape
server.jschanges. The adapter abstraction handles this entirely via the existinggetOidcProvider()hook.BaseOidcTokenProvider(REFRESH_FACTOR=0.75, auto-schedule, retry-on-failure). Inherited free.awf --env-allforwardsACTIONS_ID_TOKEN_REQUEST_URLandACTIONS_ID_TOKEN_REQUEST_TOKENto the api-proxy container; the AWS/GCP providers inproviders/openai.jsalready consume them this way.sk-ant-oat01-...never enters the agent container — same property the static-key path provides today.