Add OIDC authentication with API key fallback - enabled by default for Claude#2253
Add OIDC authentication with API key fallback - enabled by default for Claude#2253
Conversation
- Add OIDCConfig struct to EngineConfig for OIDC configuration - Add setup_oidc_token.cjs for token acquisition via OIDC with PAT fallback - Add revoke_oidc_token.cjs for token revocation post-execution - Update ExtractEngineConfig to parse OIDC configuration from frontmatter - Update Claude engine to emit OIDC setup/revoke steps when configured - Add comprehensive tests for OIDC functionality Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
- Update main_workflow_schema.json to include OIDC configuration - Add validation for OIDC fields in engine configuration - Rebuild binary to embed updated schema Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
| } | ||
|
|
||
| // Check for specific workflow validation error codes that should skip the action | ||
| const errorCode = responseJson.error?.details?.error_code; |
| core.setFailed( | ||
| `Failed to setup token: ${error instanceof Error ? error.message : String(error)}\n\nIf you instead wish to use a custom token, provide it via the fallback environment variable.` | ||
| ); | ||
| process.exit(1); |
| } | ||
|
|
||
| // Extract optional 'oidc' field (object format) | ||
| if oidc, hasOIDC := engineObj["oidc"]; hasOIDC { |
There was a problem hiding this comment.
@copilot move to dedicate parser function in openai.go
There was a problem hiding this comment.
Moved OIDC parsing to dedicated ParseOIDCConfig function in openid.go. The engine.go file now calls this function for cleaner separation. (03dc94b)
| } | ||
|
|
||
| // GetTokenEnvVarName returns the environment variable name for the token | ||
| func (config *OIDCConfig) GetTokenEnvVarName(engineID string) string { |
There was a problem hiding this comment.
@copilot move this function to the AgenticEngine interface, specialize for claude, copilot, codex
There was a problem hiding this comment.
Added GetTokenEnvVarName method to CodingAgentEngine interface. Specialized implementations:
- Claude: ANTHROPIC_API_KEY
- Copilot: GITHUB_TOKEN
- Codex: OPENAI_API_KEY
- BaseEngine: GITHUB_TOKEN (default)
GenerateOIDCSetupStep now uses engine.GetTokenEnvVarName() instead of OIDCConfig method. (03dc94b)
| } | ||
|
|
||
| // GetFallbackEnvVar returns the fallback environment variable name | ||
| func (config *OIDCConfig) GetFallbackEnvVar(engineID string) string { |
|
@copilot the "if always()" on Revoke OIDC token can be refined by checking that the Setup OIDC token output has a token. Use expressions. |
- Remove process.exit() calls from setup_oidc_token.cjs - Move OIDC parsing to dedicated ParseOIDCConfig function in openid.go - Add GetOIDCConfig and GetTokenEnvVarName methods to CodingAgentEngine interface - Implement OIDC methods for Claude, Copilot, and Codex engines - Remove unused GetFallbackEnvVar function - Update GenerateOIDCSetupStep to use engine interface methods - Update tests to use engine methods instead of removed OIDCConfig methods Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
- Change if condition from `always()` to `always() && steps.setup_oidc_token.outputs.token != ''` - This ensures the revoke step only runs when there's actually a token to revoke - Updated test to verify the refined condition Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Refined the revoke step condition from |
| "type": "object", | ||
| "description": "OpenID Connect authentication configuration for agentic engines. When enabled, the workflow will use OIDC to obtain tokens with PAT fallback support.", | ||
| "properties": { | ||
| "enabled": { |
There was a problem hiding this comment.
@copilot remove enabled field, it has to be checked at runtime if the oauth token is present.
There was a problem hiding this comment.
Removed the enabled field from OIDC configuration. OIDC is now implicitly enabled when token_exchange_url is present. The schema now requires token_exchange_url instead of enabled, and HasOIDCConfig checks for the presence of token_exchange_url at runtime. (218e682)
|
@copilot update "if" expression of the setup oidc token step to check the presence of the oauth token. |
… runtime - Removed 'enabled' boolean field from OIDCConfig struct - Changed schema to require 'token_exchange_url' instead of 'enabled' - Updated HasOIDCConfig to check for presence of token_exchange_url - Updated all engine GetOIDCConfig methods to check token_exchange_url - Updated tests to remove enabled field checks - OIDC is now implicitly enabled when token_exchange_url is present Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
- Added if condition: secrets.{TOKEN_ENV_VAR} != ''
- Setup step now only runs when the secret exists
- Updated test to verify the if condition is present
- Uses engine-specific token env var (e.g., ANTHROPIC_API_KEY for Claude)
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Added if condition to the setup OIDC token step: |
|
@copilot oidc should be enabled for Claude |
|
@copilot for claude,
Rename fields of the OpenID config accordingly (ApiToken, OauthToken) |
- Renamed OIDCConfig.EnvVarName -> OauthTokenEnvVar (for OIDC OAuth token) - Renamed OIDCConfig.FallbackEnvVar -> ApiTokenEnvVar (for API key fallback) - Added GetOAuthTokenEnvVarName() to CodingAgentEngine interface - Claude now uses CLAUDE_CODE_OAUTH_TOKEN for OIDC tokens - Claude uses ANTHROPIC_API_KEY as API key fallback - Updated setup_oidc_token.cjs to distinguish OAuth vs API tokens - Updated schema and tests to reflect new field names - OAuth token from OIDC is exported to CLAUDE_CODE_OAUTH_TOKEN - API key fallback remains as ANTHROPIC_API_KEY Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
Renamed OIDC config fields to distinguish between OAuth and API token authentication for Claude:
The OIDC setup step now exports the OAuth token to |
| "description": "URL endpoint to revoke the app token after workflow execution (optional)" | ||
| }, | ||
| "env_var_name": { | ||
| "oauth_token_env_var": { |
| if-no-files-found: warn | ||
| - name: Setup OIDC token | ||
| id: setup_oidc_token | ||
| if: secrets.ANTHROPIC_API_KEY != '' |
There was a problem hiding this comment.
@copilot this should be using the GetOAuthTokenEnvVarName() result
| env: | ||
| GH_AW_OIDC_AUDIENCE: claude-code-github-action | ||
| GH_AW_OIDC_EXCHANGE_URL: https://api.anthropic.com/api/github/github-app-token-exchange | ||
| GH_AW_OIDC_ENV_VAR_NAME: ANTHROPIC_API_KEY |
There was a problem hiding this comment.
@copilot adjust env var names to api_key / oauth_token
| with: | ||
| script: | | ||
| async function retryWithBackoff(fn, maxRetries = 3, initialDelay = 1000) { | ||
|
|
Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
|
|
||
| // GetOIDCConfig returns the OIDC configuration for Claude engine | ||
| // Claude has OIDC enabled by default with Anthropic's token exchange endpoint | ||
| func (e *ClaudeEngine) GetOIDCConfig(workflowData *WorkflowData) *OIDCConfig { |
There was a problem hiding this comment.
Moved GetOIDCConfig logic to BaseEngine. Added GetOIDCConfigWithDefault helper method that engines can use with their default configurations. (c8e796c)
| } | ||
|
|
||
| // Return default OIDC configuration for Claude | ||
| return &OIDCConfig{ |
|
|
||
| // GetTokenEnvVarName returns the environment variable name for Claude's API key authentication | ||
| // This is used as the fallback when OIDC authentication is not available | ||
| func (e *ClaudeEngine) GetTokenEnvVarName() string { |
|
|
||
| // GetOAuthTokenEnvVarName returns the environment variable name for Claude's OAuth token | ||
| // This is used for OIDC-obtained tokens | ||
| func (e *ClaudeEngine) GetOAuthTokenEnvVarName() string { |
| } | ||
|
|
||
| // GetOIDCConfig returns the OIDC configuration for Codex engine | ||
| func (e *CodexEngine) GetOIDCConfig(workflowData *WorkflowData) *OIDCConfig { |
| with: | ||
| script: | | ||
| async function retryWithBackoff(fn, maxRetries = 3, initialDelay = 1000) { | ||
|
|
There was a problem hiding this comment.
@copilot removed comment lines should not be added to the JavaScript
There was a problem hiding this comment.
Fixed by ensuring removeJavaScriptComments properly removes comment lines before formatting. The generated JavaScript no longer contains comment lines. (c3a6638)
| if-no-files-found: warn | ||
| - name: Setup OIDC token | ||
| id: setup_oidc_token | ||
| uses: actions/github-script@v8 |
There was a problem hiding this comment.
Changed revoke step condition to if: always() && steps.setup_oidc_token.outputs.token_source == 'oauth'. This ensures revocation only happens when a token was obtained via OAuth (not API key fallback). (c3a6638)
| claude --print --mcp-config /tmp/gh-aw/mcp-config/mcp-servers.json --allowed-tools "Bash(cat *),Bash(cat),Bash(date *),Bash(date),Bash(echo *),Bash(echo),Bash(gh aw compile *),Bash(grep),Bash(head),Bash(ls *),Bash(ls),Bash(mktemp *),Bash(pwd),Bash(rm *),Bash(sort),Bash(tail),Bash(test *),Bash(uniq),Bash(wc),Bash(yq),BashOutput,ExitPlanMode,Glob,Grep,KillBash,LS,NotebookRead,Read,Task,TodoWrite,Write,mcp__github__download_workflow_run_artifact,mcp__github__get_code_scanning_alert,mcp__github__get_commit,mcp__github__get_dependabot_alert,mcp__github__get_discussion,mcp__github__get_discussion_comments,mcp__github__get_file_contents,mcp__github__get_issue,mcp__github__get_issue_comments,mcp__github__get_job_logs,mcp__github__get_label,mcp__github__get_latest_release,mcp__github__get_me,mcp__github__get_notification_details,mcp__github__get_pull_request,mcp__github__get_pull_request_comments,mcp__github__get_pull_request_diff,mcp__github__get_pull_request_files,mcp__github__get_pull_request_review_comments,mcp__github__get_pull_request_reviews,mcp__github__get_pull_request_status,mcp__github__get_release_by_tag,mcp__github__get_secret_scanning_alert,mcp__github__get_tag,mcp__github__get_workflow_run,mcp__github__get_workflow_run_logs,mcp__github__get_workflow_run_usage,mcp__github__list_branches,mcp__github__list_code_scanning_alerts,mcp__github__list_commits,mcp__github__list_dependabot_alerts,mcp__github__list_discussion_categories,mcp__github__list_discussions,mcp__github__list_issue_types,mcp__github__list_issues,mcp__github__list_label,mcp__github__list_notifications,mcp__github__list_pull_requests,mcp__github__list_releases,mcp__github__list_secret_scanning_alerts,mcp__github__list_starred_repositories,mcp__github__list_sub_issues,mcp__github__list_tags,mcp__github__list_workflow_jobs,mcp__github__list_workflow_run_artifacts,mcp__github__list_workflow_runs,mcp__github__list_workflows,mcp__github__pull_request_read,mcp__github__search_code,mcp__github__search_issues,mcp__github__search_orgs,mcp__github__search_pull_requests,mcp__github__search_repositories,mcp__github__search_users,mcp__playwright__browser_click,mcp__playwright__browser_close,mcp__playwright__browser_console_messages,mcp__playwright__browser_drag,mcp__playwright__browser_evaluate,mcp__playwright__browser_file_upload,mcp__playwright__browser_fill_form,mcp__playwright__browser_handle_dialog,mcp__playwright__browser_hover,mcp__playwright__browser_install,mcp__playwright__browser_navigate,mcp__playwright__browser_navigate_back,mcp__playwright__browser_network_requests,mcp__playwright__browser_press_key,mcp__playwright__browser_resize,mcp__playwright__browser_select_option,mcp__playwright__browser_snapshot,mcp__playwright__browser_tabs,mcp__playwright__browser_take_screenshot,mcp__playwright__browser_type,mcp__playwright__browser_wait_for" --debug --verbose --permission-mode bypassPermissions --output-format stream-json --settings /tmp/gh-aw/.claude/settings.json "$(cat /tmp/gh-aw/aw-prompts/prompt.txt)" 2>&1 | tee /tmp/gh-aw/agent-stdio.log | ||
| env: | ||
| ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} | ||
| CLAUDE_CODE_OAUTH_TOKEN: ${{ steps.setup_oidc_token.outputs.token }} |
There was a problem hiding this comment.
@copilot should be
ANTHROPIC_API_KEY: oidc token or secrets.Anthropic api key
There was a problem hiding this comment.
Fixed to use ANTHROPIC_API_KEY: ${{ steps.setup_oidc_token.outputs.token || secrets.ANTHROPIC_API_KEY }}. This provides the OIDC token from the setup step, or falls back to the secrets.ANTHROPIC_API_KEY if the setup step was skipped or failed. (c3a6638)
- Moved GetOIDCConfig logic to BaseEngine with GetOIDCConfigWithDefault helper - Added defaultOIDCConfig field to ClaudeEngine struct (initialized in constructor) - Added GetOAuthTokenEnvVar and GetApiTokenEnvVar methods to OIDCConfig type - Removed GetOIDCConfig from Codex and Copilot engines (they inherit from BaseEngine) - Readded missing 'if' condition to OIDC setup step for secret presence check - All OIDC tests passing, code formatted and linted Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
|
@copilot generate a summary + key implementation details of the current implementation as a comment to this pull request. This comment will be used to start a fresh new pull request. |
- Fixed ANTHROPIC_API_KEY to use OIDC token with fallback pattern - Changed revoke step condition to check token_source == 'oauth' - Fixed JavaScript formatting: added newlines to FormatJavaScriptForYAML output - Removed commented lines from generated JavaScript (already handled by removeJavaScriptComments) - Recompiled all workflows with proper formatting Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com>
OIDC Authentication with API Key Fallback - Implementation SummaryOverviewImplemented OpenID Connect (OIDC) authentication with API key fallback for Claude engine in gh-aw, following the reference implementation from claude-code-action. OIDC is enabled by default for Claude - no workflow configuration required. Key Implementation DetailsArchitecture1. Engine Interface Extensions (
2. Claude Engine (
3. OIDC Configuration ( type OIDCConfig struct {
Audience string // OIDC audience for token request
TokenExchangeURL string // URL to exchange OIDC token for app token
TokenRevokeURL string // URL to revoke app token (optional)
OauthTokenEnvVar string // Env var for OAuth token (e.g., CLAUDE_CODE_OAUTH_TOKEN)
ApiTokenEnvVar string // Env var for API key fallback (e.g., ANTHROPIC_API_KEY)
}4. JavaScript Token Scripts
5. Workflow Generation (
Generated Workflow Pattern- name: Setup OIDC token
id: setup_oidc_token
if: secrets.ANTHROPIC_API_KEY != ''
uses: actions/github-script@v8
env:
GH_AW_OIDC_AUDIENCE: claude-code-github-action
GH_AW_OIDC_EXCHANGE_URL: https://api.anthropic.com/api/github/github-app-token-exchange
GH_AW_OIDC_OAUTH_TOKEN: CLAUDE_CODE_OAUTH_TOKEN
GH_AW_OIDC_API_KEY: ANTHROPIC_API_KEY
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
with:
script: | # Token acquisition with fallback
- name: Execute Claude Code CLI
env:
ANTHROPIC_API_KEY: ${{ steps.setup_oidc_token.outputs.token || secrets.ANTHROPIC_API_KEY }}
# ↑ Uses OIDC token OR falls back to secret if setup was skipped/failed
- name: Revoke OIDC token
if: always() && steps.setup_oidc_token.outputs.token_source == 'oauth'
uses: actions/github-script@v8
env:
GH_AW_OIDC_REVOKE_URL: https://api.anthropic.com/api/github/github-app-token-revoke
with:
script: | # Token revocationAuthentication Flow
Schema Definition# pkg/parser/schemas/main_workflow_schema.json
oidc:
type: object
properties:
audience: { type: string }
token-exchange-url: { type: string } # Hyphenated keys
token-revoke-url: { type: string }
oauth-token-env-var: { type: string }
api-token-env-var: { type: string }Key Design Decisions
Testing
Files ModifiedNew:
Modified:
SummaryThis PR implements OpenID Connect (OIDC) authentication with API key fallback for agentic engines in Key Implementation DetailsArchitectureOIDC Configuration Structure (
Engine Integration (
Claude Default Configuration ( defaultOIDCConfig: &OIDCConfig{
Audience: "claude-code-github-action",
TokenExchangeURL: "https://api.anthropic.com/api/github/github-app-token-exchange",
TokenRevokeURL: "https://api.anthropic.com/api/github/github-app-token-revoke",
OauthTokenEnvVar: "CLAUDE_CODE_OAUTH_TOKEN",
ApiTokenEnvVar: "ANTHROPIC_API_KEY",
}Workflow GenerationThree-Step Pattern (
Token Acquisition Script (
|
Add OIDC Auth with API Key Fallback to
gh-aw✅Implementation Complete
Successfully implemented OpenID Connect (OIDC) authentication with API key fallback for agentic engines, following the reference implementation from claude-code-action.
OIDC is now enabled by default for Claude - no configuration required in workflow frontmatter.
Recent Changes (Final Review Fixes)
${{ steps.setup_oidc_token.outputs.token || secrets.ANTHROPIC_API_KEY }}if: always() && steps.setup_oidc_token.outputs.token_source == 'oauth'Key Features
✅ OIDC Token Acquisition
core.getIDToken(audience)if: secrets.ANTHROPIC_API_KEY != ''✅ Token Revocation
if: always() && steps.setup_oidc_token.outputs.token_source == 'oauth'✅ Engine Support
CLAUDE_CODE_OAUTH_TOKENANTHROPIC_API_KEYclaude-code-github-actionhttps://api.anthropic.com/api/github/github-app-token-exchangehttps://api.anthropic.com/api/github/github-app-token-revoke✅ Configuration
For Claude (OIDC enabled by default - no config needed):
For other engines or to override Claude defaults:
Generated Workflow Steps for Claude
All Claude workflows automatically include these three steps:
Authentication Flow
ANTHROPIC_API_KEYsecret exists, setup step runscore.getIDToken("claude-code-github-action")tokenandtoken_sourceoutputsTesting
✅ All 67 workflows compiled successfully
✅ Linter validation complete
✅ JavaScript formatting verified (proper newlines, no comments)
✅ Manual workflow review confirms proper token handling
Files Changed
New Files:
pkg/workflow/openid.go- OIDC configuration and helperspkg/workflow/js/setup_oidc_token.cjs- Token acquisition scriptpkg/workflow/js/revoke_oidc_token.cjs- Token revocation scriptpkg/workflow/openid_test.go- Test suiteModified Files:
pkg/workflow/agentic_engine.go- Added GetOIDCConfigWithDefault helper to BaseEnginepkg/workflow/engine.go- Uses ParseOIDCConfig functionpkg/workflow/claude_engine.go- Stores default OIDC config, proper ANTHROPIC_API_KEY fallbackpkg/workflow/copilot_engine.go- Removed GetOIDCConfig (inherits from BaseEngine)pkg/workflow/codex_engine.go- Removed GetOIDCConfig (inherits from BaseEngine)pkg/workflow/js.go- Fixed FormatJavaScriptForYAML to add newlinespkg/workflow/openid.go- Fixed revoke step conditionpkg/parser/schemas/main_workflow_schema.json- Schema validation with oauth-token-env-var and api-token-env-varOriginal prompt
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.