Skip to content

Latest commit

 

History

History
749 lines (566 loc) · 45.8 KB

auth-matrix.md

File metadata and controls

749 lines (566 loc) · 45.8 KB
title Authentication matrix
description Provider-by-provider reference for static keys, OIDC federation, headers, targets, and credential isolation in the AWF API proxy.

Authentication matrix

This document describes every authentication combination supported by AWF's api-proxy sidecar, including how each provider's auth works, what configuration is required, and how the proxy transforms credentials before forwarding to upstream APIs.

The Auth Doctor Updater workflow periodically compares this matrix with current default-branch implementation, recent issues and pull requests, and official provider guidance. It opens a deduplicated, file-bounded pull request only when an evidence-backed documentation correction is needed.

Table of Contents


Dimensions Overview

Auth evaluation in the api-proxy is determined by the combination of these independent axes:

# Dimension Controlled By Values
1 Engine Port binding (10000–10004) openai, anthropic, copilot, gemini, vertex
2 Auth Type AWF_AUTH_TYPE api-key (default), github-oidc
3 OIDC Provider AWF_AUTH_PROVIDER azure, aws, gcp, anthropic
4 Instance Type GITHUB_SERVER_URL github.com, GHEC (*.ghe.com), GHES
5 BYOK Mode COPILOT_PROVIDER_TYPE unset (standard), azure
6 Target Override {PROVIDER}_API_TARGET Any hostname
7 Custom Auth Header AWF_{PROVIDER}_AUTH_HEADER Any valid HTTP header name
8 Extra Injection AWF_BYOK_EXTRA_HEADERS, AWF_BYOK_EXTRA_BODY_FIELDS JSON objects

:::note The OIDC Provider dimension (AWF_AUTH_PROVIDER) applies to OpenAI, Anthropic, Copilot, Gemini, and Vertex AI when the listed provider supports that cloud's token format. The Gemini and Vertex AI adapters support AWF_AUTH_PROVIDER=gcp through Google Workload Identity Federation; other OIDC providers do not apply to those Google adapters. :::


Provider: OpenAI

Port: 10000
Implementation: containers/api-proxy/providers/openai.js

Static API Key

Setting Value
Env var OPENAI_API_KEY
Header sent upstream Authorization: Bearer <key>
Default target api.openai.com
Default base path /v1

Official docs: https://platform.openai.com/docs/api-reference/authentication

Azure OpenAI (BYOK via Copilot)

When COPILOT_PROVIDER_TYPE=azure and COPILOT_PROVIDER_BASE_URL is set:

Setting Value
Env var COPILOT_PROVIDER_API_KEY
Header sent upstream api-key: <key> (NOT Authorization:)
Target Derived from COPILOT_PROVIDER_BASE_URL
Base path Derived from URL path component

Official docs: https://learn.microsoft.com/en-us/azure/foundry/openai/reference

Azure OIDC (Entra ID)

When AWF_AUTH_TYPE=github-oidc and AWF_AUTH_PROVIDER=azure:

Setting Value
Header sent upstream Authorization: Bearer <token>
Token exchange GitHub JWT → Azure AD token endpoint
Scope https://cognitiveservices.azure.com/.default (default; configurable via AWF_AUTH_AZURE_SCOPE)
OIDC audience api://AzureADTokenExchange (configurable via AWF_AUTH_OIDC_AUDIENCE)

Note: When Azure OIDC is active, the header switches from api-key: back to Authorization: Bearer.

:::note[Implementation vs. provider documentation] https://cognitiveservices.azure.com/.default is AWF's hardcoded default scope and remains valid for Azure OpenAI/Foundry resources. Some newer Microsoft Foundry how-to guides show https://ai.azure.com/.default for certain data-plane operations — this is not a universal replacement, and either value may be required depending on your resource and API surface. Set AWF_AUTH_AZURE_SCOPE (or apiProxy.auth.azureScope) explicitly if your deployment needs a different scope; AWF does not infer the correct scope for you. :::

Official docs: https://learn.microsoft.com/en-us/azure/foundry-classic/openai/how-to/managed-identity

:::note[Implementation vs. provider documentation] OpenAI's own API now offers native workload identity federation, which exchanges an external OIDC/JWT identity at https://auth.openai.com/oauth/token for a short-lived OpenAI access token. AWF does not implement this. The openai.js adapter's AWF_AUTH_PROVIDER OIDC support (azure, aws, gcp) is for reaching Azure OpenAI/Foundry or GCP-fronted OpenAI-compatible endpoints using that cloud's own identity tokens — it is unrelated to OpenAI's native federation feature. :::

Custom Auth Header

AWF_OPENAI_AUTH_HEADER overrides the header name used for the API key. The raw key or token value is sent directly as the header value (no Bearer prefix is added), both for static keys and OIDC tokens.


Provider: Anthropic

Port: 10001
Implementation: containers/api-proxy/providers/anthropic.js

Static API Key

Setting Value
Env var ANTHROPIC_API_KEY
Header sent upstream x-api-key: <key>
Default target api.anthropic.com
Default base path (none)

Additional required headers: anthropic-version: 2023-06-01

Official docs: https://platform.claude.com/docs/en/api/overview

:::note[Implementation vs. provider documentation] Anthropic SDKs officially support ANTHROPIC_AUTH_TOKEN for bearer-token authentication. AWF does not currently accept that variable as a host-side source credential: static Anthropic auth is read from ANTHROPIC_API_KEY and sent as x-api-key. In the agent container, AWF reserves ANTHROPIC_AUTH_TOKEN for the non-secret placeholder sk-ant-placeholder-key-for-credential-isolation; the real credential remains in the sidecar. :::

Workload Identity Federation (WIF)

When AWF_AUTH_TYPE=github-oidc and AWF_AUTH_PROVIDER=anthropic:

Setting Value
Header sent upstream Authorization: Bearer <wif_token> (NOT x-api-key)
Token exchange endpoint POST https://api.anthropic.com/v1/oauth/token
Grant type urn:ietf:params:oauth:grant-type:jwt-bearer
Required config AWF_AUTH_ANTHROPIC_FEDERATION_RULE_ID, AWF_AUTH_ANTHROPIC_ORGANIZATION_ID, AWF_AUTH_ANTHROPIC_SERVICE_ACCOUNT_ID
Optional config AWF_AUTH_ANTHROPIC_WORKSPACE_ID, AWF_AUTH_ANTHROPIC_TOKEN_URL
OIDC audience https://api.anthropic.com (default, configurable)
Token format sk-ant-oat01-... (short-lived)

Key behavior change: When OIDC is active, the auth header switches from x-api-key to Authorization: Bearer.

:::note[Anthropic beta headers] AWF follows Anthropic's SDK behavior: JWT-bearer POST /v1/oauth/token exchanges send oauth-2025-04-20,oidc-federation-2026-04-01, while API requests authenticated with the resulting bearer token send oauth-2025-04-20. The federation beta is never added to static x-api-key requests or forwarded refresh-token exchanges. Client-supplied anthropic-beta values are preserved and deduplicated with AWF-required values and the optional auto-cache beta. :::

Official references: Anthropic WIF documentation · Anthropic TypeScript SDK federation exchange

Custom Auth Header

AWF_ANTHROPIC_AUTH_HEADER overrides the header name (default: x-api-key). Useful for enterprise gateways that expect a different header.


Provider: GitHub Copilot

Port: 10002
Implementation: containers/api-proxy/providers/copilot.js, containers/api-proxy/providers/copilot-auth.js

GitHub OAuth Token (Standard)

Instance Env var Header Target
github.com COPILOT_GITHUB_TOKEN Authorization: Bearer <token> api.githubcopilot.com
GHEC (*.ghe.com) COPILOT_GITHUB_TOKEN Authorization: Bearer <value> copilot-api.<subdomain>.ghe.com
GHES (on-prem) COPILOT_GITHUB_TOKEN Authorization: token <value> ⚠️ api.enterprise.githubcopilot.com
Business tier COPILOT_GITHUB_TOKEN Authorization: token <value> ⚠️ api.business.githubcopilot.com (must be set explicitly via COPILOT_API_TARGET; never auto-derived)

:::note[Implementation vs. provider documentation] GitHub's REST API authentication docs state that both Bearer-prefixed and token-prefixed Authorization headers are generally accepted for PATs/OAuth tokens (only JWTs strictly require Bearer). The token prefix requirement documented here is AWF-implementation-specific defensive behavior for Copilot API requests, not a general GitHub REST API rule: the enterprise target (api.enterprise.githubcopilot.com) and the business target (api.business.githubcopilot.com) return 400 Bad Request: Authorization header is badly formatted when sent the wrong prefix. AWF detects this via copilotTargetRequiresGitHubTokenPrefix() in copilot-auth.js, which matches on the specific target hostname or GHES-detection heuristics (AWF_PLATFORM_TYPE=ghes, or a GITHUB_SERVER_URL that isn't github.com/*.ghe.com). GHEC data-residency targets (copilot-api.<subdomain>.ghe.com) use the standard Bearer prefix. BYOK keys always use the Bearer prefix regardless of target.

Fine-grained PAT override: As of PR #8038, COPILOT_GITHUB_TOKEN values that start with github_pat_ (fine-grained PATs) always use the Bearer prefix, on every Copilot target including GHEC/GHES/Business — they never fall back to token <value>, even where classic PATs and OAuth tokens on those targets require it. This override lives in getGitHubTokenAuthPrefix() in copilot-auth.js and is covered by copilot-adapter-enterprise.test.js and copilot-auth.test.js. :::

Copilot API host requests also include Copilot-Integration-Id. The default is agentic-workflows; set COPILOT_INTEGRATION_ID to override it, or set GITHUB_COPILOT_INTEGRATION_ID as a fallback (checked only when COPILOT_INTEGRATION_ID is unset) — added in PR #8038.

Prompt-cache and attribution headers (*.githubcopilot.com only)

When the resolved upstream host is a Copilot host (githubcopilot.com or a *.githubcopilot.com subdomain), the sidecar guarantees two non-secret headers are present on every forwarded request:

Header Value Purpose
X-Interaction-Id ${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}, else a UUID minted once per sidecar process CAPI prompt-cache key — must be stable for a whole run and differ across runs
Copilot-Integration-Id COPILOT_INTEGRATION_ID, else GITHUB_COPILOT_INTEGRATION_ID, else agentic-workflows Attribution, quota bucket, and model allowlist

Non-empty inbound values are preserved; empty values and case-variant duplicates are replaced so exactly one instance of each header reaches CAPI. Harnesses that do not send a stable X-Interaction-Id themselves (aider, Pi, …) therefore still get prompt-cache hits, and a harness that owns its own session identity can override the value simply by sending the header. There is no AWF-specific override env var for X-Interaction-Id: outside GitHub Actions the sidecar mints one UUID per process. Neither header is injected on non-Copilot hosts, so BYOK targets (Azure OpenAI, OpenRouter, …) and the OpenAI/Anthropic/Gemini providers are unaffected.

Auto API Version Injection (*.githubcopilot.com only)

For GitHub Copilot catalog targets, the sidecar auto-selects appropriate x-github-api-version headers for certain endpoints to ensure API compatibility:

Endpoint HTTP Method Auto-Selected Version Notes
/auto POST 2026-08-01 Copilot Auto inference endpoint
/models/session POST 2025-07-16 Model session initialization
/models/session/intent POST 2025-07-16 Model session intent parsing
/models GET 2026-07-01 Model catalog listing (see below); applied for GitHub Copilot catalog targets when COPILOT_GITHUB_TOKEN is available, via a path separate from the POST method check

Key behaviors:

  • Auto-injection applies only to GitHub Copilot targets (*.githubcopilot.com); BYOK and non-Copilot targets are unaffected.
  • If the request already includes an x-github-api-version header (case-insensitive), it is preserved and auto-injection is skipped — this applies to the POST endpoints above (getDefaultAutoApiVersion() in copilot.js).
  • POST requests to other endpoints do not receive auto-injected versions; callers must specify versions for other endpoints if needed.
  • /models GET is handled by a separate, unconditional code path (buildCopilotModelsRequest() and the request-header hook in copilot.js), not by the POST-gated getDefaultAutoApiVersion() logic above — it always attaches X-GitHub-Api-Version: 2026-07-01 for GitHub Copilot catalog targets.

/models Endpoint (Special Case)

The /models endpoint prefers COPILOT_GITHUB_TOKEN (GitHub OAuth) over BYOK keys when both are configured, because model listing is a GitHub platform feature. However, when no GitHub token is available (typical for direct-BYOK/custom targets), /models will use the BYOK credential. For GitHub Copilot catalog targets, /models GET requests always receive X-GitHub-Api-Version: 2026-07-01 (COPILOT_MODELS_API_VERSION in copilot.js), verified by copilot-adapter-enterprise.test.js and copilot-byok.test.js.

BYOK (Bring Your Own Key)

Setting Value
Env var COPILOT_PROVIDER_API_KEY
Header Authorization: Bearer <key> (always Bearer, even on GHES)
Target From COPILOT_PROVIDER_BASE_URL or COPILOT_API_TARGET

Renewable token broker (experimental Copilot inference)

Select --copilot-token-broker or apiProxy.copilotTokenBroker: true explicitly. An inherited GH_AW_TOKEN_BROKER_URL alone does not select broker authentication. This mode routes the agent to the Copilot sidecar on port 10002 with non-secret placeholder authentication, even without a static Copilot credential. Static-token and BYOK behavior is unchanged when the mode is not selected.

The runtime host must supply:

Setting Meaning
GH_AW_TOKEN_BROKER_URL Complete absolute HTTPS exchange endpoint and exact OIDC audience
ACTIONS_ID_TOKEN_REQUEST_URL / ACTIONS_ID_TOKEN_REQUEST_TOKEN Actions-owned OIDC bootstrap, used only by the trusted sidecar
GITHUB_REPOSITORY_ID, GITHUB_REPOSITORY_OWNER_ID Stable IDs partitioning trusted local credential state
GITHUB_RUN_ID, GITHUB_RUN_ATTEMPT, GITHUB_JOB Consuming execution context; not server authorization evidence

Keep the broker URL in runtime Actions variables/secrets, not checked-in AWF configuration or compiled deployment URLs. A selected mode with missing configuration fails closed. Copilot static credentials, BYOK/cloud authentication, custom destinations/base paths, cross-provider fallback, and experimental model routing are conflicting configurations in broker mode, not recovery mechanisms. Agent enclaves are also rejected: their dedicated proxy deliberately excludes the Actions OIDC bootstrap required by broker mode. Script-only enclaves remain supported. Broker responses cannot select an inference destination or tenant. Broker mode defaults to api.githubcopilot.com; explicit targets are limited to that host, api.individual.githubcopilot.com, api.business.githubcopilot.com, and api.enterprise.githubcopilot.com, using HTTPS port 443 without a base path. Listing an approved destination is not a promise that the preview token works there. GHEC tenant destinations require separate reviewed broker support; non-broker enterprise routing is unchanged.

Compiler/runtime handoff

github/gh-aw must map boolean token-broker.copilot-request: true to --copilot-token-broker (or the equivalent AWF configuration member). It must resolve the URL at Actions runtime:

env:
  GH_AW_TOKEN_BROKER_URL: ${{ vars.GH_AW_TOKEN_BROKER_URL || secrets.GH_AW_TOKEN_BROKER_URL }}

Only consuming jobs need id-token: write, within the caller's permission envelope. Do not combine the inference selection with permissions.copilot-requests: write. Preserve separate copilot-task handling; an inference installation token does not establish authorization for coding-agent task creation or assignment.

This AWF change does not remove the upstream compiler's main-agent inference guard, including its applicable fallback-model guard. That change belongs in github/gh-aw after a compatible AWF image/runtime and a real integrated deployment are verified. Bounded detection/evals exchanges are not evidence of renewable main-agent inference.

Trust boundary and transport

The host selects the mode, validates configuration, and passes the bootstrap, broker endpoint, and execution context only to the isolated API-proxy container. The sidecar owns OIDC acquisition, exchange, in-memory caching, and renewal. The agent receives neither broker configuration nor OIDC bootstrap/JWTs nor real inference credentials, including with environment passthrough enabled.

Broker, OIDC and the selected Copilot target receive source-scoped CONNECT policy for the trusted sidecar, not agent domain allowlist entries. Existing blocked-network and unsafe-port guards still apply. These authentication tunnels are spliced before TLS inspection; their endpoint hosts are suppressed in Squid access logs and redacted from audit snapshots. Failure diagnostics omit the encoded Squid configuration as well. The client uses the sidecar's HTTPS proxy transport, so it does not depend on runner filesystem paths or a runner-local socket; the same handoff works with Docker-in-Docker. Deployment proxies and TLS trust must be configured through existing AWF transport/trust facilities. Do not disable TLS verification to make a broker reachable. This runtime handoff accepts GitHub.com Actions OIDC endpoints under *.actions.githubusercontent.com; other GitHub deployments need separately reviewed issuer/transport support, not wildcard enrollment. Endpoint configuration is trusted cooperative deployment policy: HTTPS alone does not establish that a repository administrator's chosen broker is approved.

Exchange and renewal contract

The client follows the experimental gh-aw Token Broker Specification 1.0.0. The configured URL, including case, path, query and explicit port, is the exact OIDC audience. No suffix/discovery route is appended; no redirects are followed on either hop. An exchange is one HTTPS POST with JSON content type, JSON accept header, and Cache-Control: no-store:

{"token_type":"copilot-request","provider":"github","oidc_token":"<fresh-actions-jwt>"}

Success is HTTP 200 with Content-Type: application/json and Cache-Control: no-store, containing exactly two unique string members:

{"token":"<opaque-installation-token>","expires_at":"2026-10-30T18:00:00Z"}

Tokens are nonempty printable ASCII U+0021–U+007E; no prefix or fixed length is assumed. Responses cannot contain endpoints, scopes or tenant metadata. Duplicate members, additional/missing members, unsafe tokens, invalid UTF-8/JSON, and invalid calendar dates are rejected. Expiration uses the specification's restricted RFC 3339 profile: lowercase t/z, fractions, and numeric offsets are accepted; leap seconds, hour 24, and invalid time/offset components are not.

Both response reads and the request body are bounded to 65,536 bytes. Compressed responses are rejected; configure broker middleware to serve this small JSON response without compression. One 10-second total deadline includes OIDC acquisition, proxy/TLS connection, broker exchange, and both response reads. There is no automatic exchange retry.

Each upstream request, including model discovery, resolves authorization when it is prepared. Tokens with 30 seconds or less remaining are discarded and renewed using a fresh OIDC identity. A newly issued token must exceed that margin. Each provider instance owns one immutable endpoint/provider/capability/ repository/run/attempt/job context; concurrent requests share its in-flight exchange, but separate contexts/processes cannot inherit credentials. Shutdown clears memory and cancels pending exchanges.

Denial, malformed/near-expiry credentials, unsupported capability, redirects, timeout and outage fail closed. No static credential, alternate provider, stale token, or replay of a potentially billable inference request is an authentication workaround. Already-established streams are not stopped solely because their token expires. AWF does not guarantee the upstream will keep such a stream open: operators must measure actual stream behavior, while subsequent requests renew. HTTP streaming is supported; WebSocket upgrades fail closed in broker mode because renewable WebSocket authentication is not implemented.

Deployment-owned organization GitHub App issuer

Organization-installed GitHub Apps using Copilot are in public preview, subject to change. This path is not supported for personal-account or enterprise installations. The organization must approve the App's organization Copilot Requests: Read-only permission and installation. Read-only permits inference spending; it does not mean cost-free access.

The broker, not AWF, holds App signing credentials in protected server-side storage and chooses the organization installation from enrolled policy. After identity and policy verification, use GitHub's supported installation access token issuance with minimum approved permissions. Return the issuer's actual expiration. Installation tokens expire after one hour; there is no assumed caller-selected TTL. Advertising an earlier expiration only changes client refresh timing, not the credential's underlying lifetime. Stronger lifetime promises require supported issuer behavior or revocation.

The following is an illustrative server adapter boundary, not a broker, JWT verifier, or production SDK implementation. The injected functions must be implemented using the deployment's protected GitHub App client and policy:

async function issueCopilotRequest(verifiedContext, capability, services) {
  // Each operation below is server-owned; no installation ID comes from the caller.
  const policy = await services.authorizeEnrolledContext(verifiedContext);
  if (capability !== 'copilot-request' || !policy.githubCopilotEnabled) {
    throw services.sanitizedUnsupported();
  }
  await services.enforceReplayAndRatePolicy(verifiedContext, capability);
  const issued = await services.issueOrganizationInstallationToken({
    installationId: policy.organizationInstallationId,
    permissions: policy.minimumApprovedCopilotPermissions,
  });
  await services.validateIssuerResult(issued, policy);
  // Track this independently revocable token only in job-scoped server memory.
  await services.recordJobCredential(verifiedContext, issued);
  return { token: issued.token, expires_at: issued.expiresAt };
}

Before invoking any issuer adapter, the backend must:

  1. Verify the trusted GitHub issuer/discovery/JWKS, allowed algorithm, signature, exact single-string audience, and finite bounded iat/nbf/exp age/lifetime. JWT-supplied key URLs must not select trust roots or destinations. Support bounded trusted-key refresh/rotation; missing trust/time policy denies issuance.
  2. Bind stable owner/repository IDs to an enrolled tenant and approved workflow/reusable-workflow revision, ref, environment and event/fork policy. Verify run/attempt and consuming-job evidence through an approved signed claim profile or correlated trusted GitHub metadata. GITHUB_JOB and other caller environment variables are only local cache context, never authorization evidence.
  3. Authorize capability, installation, entitlement and approved inference destination from server policy; reject cross-tenant selection and unsupported capabilities without minting. For known types verify identity before disclosing capability support. Apply bounded abuse/replay controls and per-context rate limits without preventing independently authorized cross-capability exchanges.
  4. Enforce finite upstream timeouts, least privilege, actual expiration, issuer compatibility, and independent revocation. Fail issuance if any required evidence or result cannot be verified; never switch tenant/issuer/capability.

Organization model policies, billing and spending controls apply. There is no per-App or per-installation budget. Enable the organization's stop-spending budget behavior when required; alert-only budgets do not stop usage. The CLI organization-billing policy is a different authentication path. Suspending or uninstalling an installation blocks App access, including other organization resources, and is not a job-scoped cleanup mechanism.

Post-job invalidation and backend conformance

Lifecycle ownership is split deliberately: AWF cancels local exchanges and clears its cache; the compiler owns an unconditional, separate always() post-job step, with a fresh OIDC identity and the same endpoint/audience:

{"operation":"invalidate","oidc_token":"<fresh-actions-jwt>"}

Only HTTP 204 with empty body and Cache-Control: no-store is successful. Verify identity and exact execution context again; revoke all credentials issued to that authorized job, make repeated cleanup idempotent, and deny cross-job, run, attempt, repository and tenant invalidation without revealing existence. Do not share one cached installation token across jobs and claim it can be revoked independently. The broker needs a working per-token revocation strategy or must reject deployment conformance. Cleanup cannot replace finite credential lifetimes when a job is force-terminated.

GitHub documents DELETE /installation/token: authenticate with the particular installation token to revoke it; success is HTTP 204. A backend can retain each job's independently issued tokens in protected memory and revoke each through this endpoint, then discard them. Never accept a caller-supplied token to choose what to revoke. Handle already-revoked/expired tokens under the server's idempotent cleanup policy without hiding genuine revocation outages. Demonstrate that revoking one job's tokens leaves another job's separately issued tokens usable; SDK installation-token caches must not collapse these into a shared credential.

Backend authors should use the upstream T-TBR-002–009 and T-TBR-013–016 fixtures with a controlled issuer/JWKS, deterministic clock, isolated tenants and an instrumented issuer. Assert zero mint calls on pre-issuance rejection and no returned credential on rejected issuer results. Exercise key rotation, unknown keys, invalid audience/signature/time, unsupported capability, fork and job policy denial, replay/rate boundaries, duplicate/oversize bodies, actual expiration, repeated invalidation and denied cross-context revocation.

AWF's client fixtures are containers/api-proxy/providers/token-broker.test.js; proxy fixtures cover request-time inference/model discovery and streaming. These are client/runtime tests, not server or integrated-deployment certification. Live acceptance remains deployment-owned and unverified here: demonstrate actual model listing, Copilot inference, renewal with real Actions OIDC, long-lived stream behavior, and job-scoped revocation while another job continues using its own credential. Successful minting/exchange alone does not count.

Sanitized troubleshooting

Client classifications are ERR_TOKEN_BROKER_CONFIGURATION, OIDC, UNAUTHORIZED (401/403), UNSUPPORTED (400/404/422/501), UNAVAILABLE (transport/deadline/other status), and MALFORMED_RESPONSE, each prefixed ERR_TOKEN_BROKER_. Inspect only sanitized lifecycle/policy diagnostics. Never print JWTs, bootstrap tokens, issued tokens, full broker URLs/bodies, claim dumps or underlying exception details. Disable credential-bearing HTTP capture, proxy/APM traces and durable credential storage on the backend. Use access-controlled, retention-bounded policy diagnostics and non-sensitive test evidence; do not put credentials in workflow outputs or artifacts.

Azure BYOK through the OpenAI adapter

When COPILOT_PROVIDER_TYPE=azure, the OpenAI adapter on port 10000:

  • Header switches to api-key: <value> (Azure convention)
  • Unless OIDC is active, in which case it's Authorization: Bearer

The Copilot adapter on port 10002 does not emit api-key; its BYOK requests use Authorization: Bearer.

Copilot OIDC (Azure Entra / GCP / AWS)

When AWF_AUTH_TYPE=github-oidc with Copilot:

Provider Header Notes
Azure Authorization: Bearer <entra_token> Via oidc-token-provider.js
GCP Authorization: Bearer <gcp_token> Via gcp-oidc-token-provider.js
AWS SigV4 Authorization plus x-amz-* signing headers Via aws-oidc-token-provider.js and aws-sigv4.js

:::caution[Agent routing is credential-triggered] Cloud OIDC configuration can initialize credentials in the sidecar, but the agent's OpenAI and Copilot base URLs/placeholders are currently configured only when the corresponding static OpenAI/Copilot credential is present. Treat Azure, GCP, and AWS OIDC support for OpenAI/Copilot as sidecar authentication capability rather than a complete keyless agent-routing path until OIDC-aware agent routing is implemented for those adapters.

Three providers are exceptions with explicit OIDC-aware agent routing: Anthropic WIF (shouldProxyAnthropic() recognizes AWF_AUTH_PROVIDER=anthropic), and GCP WIF for the Gemini and Vertex AI adapters (isGcpOidcConfigured() in gemini-credential-env.ts/vertex-credential-env.ts, added in PR #8888) — all three set agent base-URL/placeholder env vars from OIDC config alone, without a static key. GCP OIDC routing for Copilot specifically remains credential-triggered only; buildCopilotCredentialEnv() does not check isGcpOidcConfigured(). :::

:::note[AWS OIDC + Copilot] Selecting AWF_AUTH_PROVIDER=aws signs Copilot-adapter HTTP requests at final dispatch with the cached temporary STS credentials. The target must be the exact regional Bedrock Runtime hostname; credentials are never returned by getAuthHeaders() or exposed to the agent. :::

Official docs:


Provider: Google Gemini

Port: 10003
Implementation: containers/api-proxy/providers/google-adapter.js (Gemini factory from the declarative google-provider-specs.js registry)

Static API Key

Setting Value
Env var GEMINI_API_KEY
Header sent upstream x-goog-api-key: <key>
Default target generativelanguage.googleapis.com
Default base path (none)

The proxy also strips ?key=, ?apiKey=, and ?api_key= query parameters from requests to prevent duplicate-key errors.

GCP Workload Identity Federation

Setting Value
Env vars AWF_AUTH_TYPE=github-oidc, AWF_AUTH_PROVIDER=gcp, AWF_AUTH_GCP_WORKLOAD_IDENTITY_PROVIDER
Header sent upstream Bearer auth header
Optional service account AWF_AUTH_GCP_SERVICE_ACCOUNT
Optional scope AWF_AUTH_GCP_SCOPE (default: https://www.googleapis.com/auth/cloud-platform) — applies only to service-account impersonation; the STS exchange always requests cloud-platform

This path keeps the GitHub OIDC exchange and short-lived GCP access token inside the api-proxy sidecar. It is intended for Gemini API endpoints that accept Google OAuth bearer tokens; billing follows the Google Cloud project/service account authorized by the workload identity pool.

:::caution[Gemini API key migration] Google says the Gemini API will reject standard API keys beginning in September 2026. Migrate GEMINI_API_KEY to Google's service-account-bound authorization key format before that deadline; AWF forwards either key type through the same x-goog-api-key header. :::

Official docs: https://ai.google.dev/gemini-api/docs/api-key


Provider: Google Vertex AI

Port: 10004 Implementation: containers/api-proxy/providers/google-adapter.js (Vertex factory from the declarative google-provider-specs.js registry)

Static API Key

Setting Value
Env var GOOGLE_API_KEY
Header sent upstream x-goog-api-key: <key>
Default target aiplatform.googleapis.com
Default base path (none)

GCP Workload Identity Federation

Setting Value
Env vars AWF_AUTH_TYPE=github-oidc, AWF_AUTH_PROVIDER=gcp, AWF_AUTH_GCP_WORKLOAD_IDENTITY_PROVIDER
Header sent upstream Bearer auth header
Optional service account AWF_AUTH_GCP_SERVICE_ACCOUNT
Optional scope AWF_AUTH_GCP_SCOPE (default: https://www.googleapis.com/auth/cloud-platform) — applies only to service-account impersonation; the STS exchange always requests cloud-platform

This is the native Vertex/Gemini CLI path for GOOGLE_GENAI_USE_VERTEXAI=true: AWF sets GOOGLE_VERTEX_BASE_URL to port 10004, exchanges the GitHub Actions OIDC token for a short-lived GCP token in the sidecar, and forwards Vertex requests to aiplatform.googleapis.com (or VERTEX_API_TARGET). Billing and quota are charged to the Google Cloud project associated with the impersonated service account, or to the directly granted workload identity principal when no service account is configured.

:::note[Implementation vs. provider documentation] This adapter exists to support the Gemini CLI's GOOGLE_GENAI_USE_VERTEXAI=true mode: setting GOOGLE_VERTEX_BASE_URL to point at this sidecar routes Vertex AI traffic through AWF. Google's general Vertex AI guidance recommends Application Default Credentials, a service account key, or workload identity federation for Vertex AI endpoints, and treats a bare API key as unsupported for most Vertex AI surfaces (API keys are the norm for the separate Gemini Developer API at generativelanguage.googleapis.com). Prefer GCP WIF for Vertex AI. AWF still forwards x-goog-api-key when GOOGLE_API_KEY is configured for deployments that explicitly rely on API-key-compatible Vertex surfaces such as Vertex AI Express Mode. :::

Official docs: https://geminicli.com/docs/get-started/authentication/


OIDC Providers

All OIDC flows require GitHub Actions runtime tokens:

  • ACTIONS_ID_TOKEN_REQUEST_URL — endpoint to mint OIDC JWTs
  • ACTIONS_ID_TOKEN_REQUEST_TOKEN — auth token for the OIDC endpoint

Documentation audits treat both as non-observable credentials: they verify id-token: write and sidecar configuration but never request or print either value, a minted JWT, or exchanged cloud credentials.

AWF forwards these variables to the api-proxy sidecar in github-oidc mode, and also when GH_AW_OTLP_WORKLOAD_IDENTITY is configured to enable OIDC workload identity for the OTLP trace exporter; both cases exclude the variables from the agent container. GitHub Agentic Workflows independently passes them from its runner-owned Start MCP Gateway step directly to the MCP gateway when a remote HTTP MCP server uses auth.type: github-oidc; AWF does not launch or configure that gateway. github/gh-aw#50053 is resolved by github/gh-aw#50054, which enforces this boundary.

Azure (Entra ID)

Config Env Var Required
Tenant ID AWF_AUTH_AZURE_TENANT_ID ✅
Client ID AWF_AUTH_AZURE_CLIENT_ID ✅
Scope AWF_AUTH_AZURE_SCOPE ❌ (default: https://cognitiveservices.azure.com/.default)
Cloud AWF_AUTH_AZURE_CLOUD ❌ (default: public; options: public, usgovernment, china)
Audience AWF_AUTH_OIDC_AUDIENCE ❌ (default: api://AzureADTokenExchange)

Token exchange endpoint: https://login.microsoftonline.com/<tenant>/oauth2/v2.0/token
Implementation: containers/api-proxy/oidc-token-provider.js
Official docs: https://learn.microsoft.com/en-us/entra/workload-id/workload-identity-federation-create-trust

AWS (STS)

Config Env Var Required
Role ARN AWF_AUTH_AWS_ROLE_ARN ✅
Region AWF_AUTH_AWS_REGION ✅
Session Name AWF_AUTH_AWS_ROLE_SESSION_NAME ❌ (default: awf-oidc-session)
Audience AWF_AUTH_OIDC_AUDIENCE ❌ (default: sts.amazonaws.com)

Token exchange: GET https://sts.<region>.amazonaws.com/?Action=AssumeRoleWithWebIdentity
Result: Temporary credentials (AccessKeyId, SecretAccessKey, SessionToken), cached and refreshed by AwsOidcTokenProvider

Request signing: SigV4 with service bedrock-runtime, applied after body transforms and repeated for retries

Implementation: containers/api-proxy/aws-oidc-token-provider.js, containers/api-proxy/aws-sigv4.js

Official docs: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_oidc.html

:::note[Signing boundary and fail-closed behavior] The request layer signs the HTTP method, canonical path and sorted query, final body SHA-256, exact regional Bedrock Runtime host, region, and bedrock-runtime service. It includes the STS session token and re-signs retries. Missing/expired credentials return 503 without opening an upstream connection. To prevent credential disclosure, other target hosts and WebSocket upgrades are rejected. :::

GCP (Workload Identity Federation)

Config Env Var Required
WIF Provider AWF_AUTH_GCP_WORKLOAD_IDENTITY_PROVIDER ✅
Service Account AWF_AUTH_GCP_SERVICE_ACCOUNT ❌ (direct federation if omitted)
Scope AWF_AUTH_GCP_SCOPE ❌ (default: https://www.googleapis.com/auth/cloud-platform; used in step 2 only)
Audience AWF_AUTH_OIDC_AUDIENCE ❌ (default: https://iam.googleapis.com/ + WIF provider)
Project AWF_AUTH_GCP_PROJECT ❌ (needed with location for native Vertex path scoping)
Location AWF_AUTH_GCP_LOCATION ❌ (needed with project for native Vertex path scoping)

When both project and location are set, native v1/v1beta1 Vertex model paths are rewritten to project-scoped paths; if either is absent, paths are left unchanged. See api-proxy-sidecar.md.

Token exchange (2-step):

  1. POST https://sts.googleapis.com/v1/token — exchange GitHub JWT for federated token
  2. POST https://iamcredentials.googleapis.com/.../:generateAccessToken — (optional) exchange for SA-scoped token

Implementation: containers/api-proxy/gcp-oidc-token-provider.js
Official docs: https://cloud.google.com/iam/docs/workload-identity-federation-with-deployment-pipelines

Anthropic (Native WIF)

Config Env Var Required
Federation Rule ID AWF_AUTH_ANTHROPIC_FEDERATION_RULE_ID ✅
Organization ID AWF_AUTH_ANTHROPIC_ORGANIZATION_ID ✅
Service Account ID AWF_AUTH_ANTHROPIC_SERVICE_ACCOUNT_ID ✅
Workspace ID AWF_AUTH_ANTHROPIC_WORKSPACE_ID ❌
Token URL AWF_AUTH_ANTHROPIC_TOKEN_URL ❌ (default: https://api.anthropic.com/v1/oauth/token)
Audience AWF_AUTH_OIDC_AUDIENCE ❌ (default: https://api.anthropic.com)

Token exchange: POST https://api.anthropic.com/v1/oauth/token (RFC 7523 jwt-bearer), with anthropic-beta: oauth-2025-04-20,oidc-federation-2026-04-01

Bearer API requests: anthropic-beta: oauth-2025-04-20 (merged with client and auto-cache beta values)

Implementation: containers/api-proxy/anthropic-oidc-token-provider.js

Official references: Anthropic WIF documentation · Anthropic TypeScript SDK credential constants


GitHub Instance Types

Detection logic in containers/api-proxy/providers/copilot-auth.js:

GITHUB_SERVER_URL → deriveCopilotApiTarget():
  *.ghe.com     → copilot-api.<subdomain>.ghe.com
  github.com    → api.githubcopilot.com
  (other)       → api.enterprise.githubcopilot.com

api.business.githubcopilot.com (Business tier) is never auto-derived from GITHUB_SERVER_URL — it must be set explicitly via COPILOT_API_TARGET.

Auth Header Prefix Rules

Target Credential Type Auth Header Format
api.githubcopilot.com GitHub token Bearer <token>
copilot-api.*.ghe.com GitHub token (classic PAT/OAuth) Bearer <value>
api.enterprise.githubcopilot.com GitHub token (classic PAT/OAuth) token <value>
api.business.githubcopilot.com GitHub token (classic PAT/OAuth) token <value>
Any target GitHub token (fine-grained PAT, github_pat_*) Bearer <value> (always, since PR #8038)
Any target BYOK key Bearer <key> (always)
Any target OIDC token Bearer <token> (always)

The token prefix is used for classic PAT/OAuth GitHub tokens on the enterprise and business Copilot targets, or when GHES is otherwise detected (see copilotTargetRequiresGitHubTokenPrefix() in copilot-auth.js). GHEC data-residency targets use the standard Bearer prefix. BYOK and OIDC always use the standard prefix. As noted above, this is AWF-implementation-specific behavior driven by observed 400 errors from those targets — not a general GitHub REST API requirement. Fine-grained PATs (github_pat_*) are detected via getGitHubTokenAuthPrefix() and always use Bearer, overriding the target-based rule above.


Custom Headers & Injection

Protected Headers (cannot be overridden)

  • authorization
  • x-api-key
  • x-goog-api-key
  • proxy-authorization

BYOK Extra Headers (AWF_BYOK_EXTRA_HEADERS)

JSON object of headers injected on BYOK inference requests:

  • Only active when COPILOT_PROVIDER_API_KEY is set
  • NOT injected on /models GET when GitHub OAuth token is available
  • Protected headers are skipped with a console.warn message

BYOK Extra Body Fields (AWF_BYOK_EXTRA_BODY_FIELDS)

JSON object of string fields merged into request body:

  • Only active in BYOK mode
  • Existing body fields are NOT overridden

Session Tracking (AWF_PROVIDER_SESSION_ID)

Adds x-session-id header automatically in BYOK mode unless already present.


Coverage Matrix

Engine Auth Mode Instance Tested Implementation
OpenAI Static key — ✅ openai.js
OpenAI Azure BYOK — ✅ openai.js
OpenAI Azure OIDC — ✅ openai.js, oidc-token-provider.js
OpenAI AWS Bedrock OIDC + SigV4 — ✅ openai.js, aws-oidc-token-provider.js, aws-sigv4.test.js
OpenAI GCP OIDC — ✅ openai.js, gcp-oidc-token-provider.js
Anthropic Static key — ✅ anthropic.js
Anthropic WIF — ✅ anthropic.js, anthropic-oidc-token-provider.js
Anthropic Custom header — ✅ anthropic.js
Copilot GitHub token github.com ✅ copilot.js, copilot-auth.js
Copilot GitHub token GHEC ✅ copilot.js, copilot-auth.js
Copilot GitHub token GHES ✅ copilot.js, copilot-auth.js
Copilot GitHub token Business tier ✅ copilot-adapter-enterprise.test.js
Copilot BYOK key — ✅ copilot.js, copilot-byok.js
Copilot Azure BYOK — ✅ via OpenAI adapter
Copilot Azure OIDC — ✅ copilot-adapter-enterprise.test.js
Copilot AWS Bedrock OIDC + SigV4 — ✅ aws-oidc-token-provider.js, server.auth-matrix.test.js
Copilot GCP OIDC — ✅ gcp-oidc-token-provider.js, server.auth-matrix.test.js
Copilot GHES + BYOK GHES ✅ server.auth-matrix.test.js
Gemini Static key — ✅ google-adapter.js, google-provider-specs.js
Gemini GCP WIF — ✅ google-adapter.js, gcp-oidc-token-provider.js, server.auth-matrix.test.js
Vertex AI Static key — ✅ google-adapter.js, google-provider-specs.js
Vertex AI GCP WIF — ✅ google-adapter.js, gcp-oidc-token-provider.js, server.auth-matrix.test.js

:::note "Implementation" column lists source files, not line numbers — line references go stale quickly as the code evolves. Use your editor's search to locate the relevant logic within each file. :::