-
Notifications
You must be signed in to change notification settings - Fork 380
Add OIDC authentication with API key fallback - enabled by default for Claude #2253
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
28e701d
e6ccd5e
dd4be20
03dc94b
00def6c
218e682
2ba5f2d
b2aaf10
ac1633f
303e5cc
c8e796c
c3a6638
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -83,11 +83,46 @@ func (e *ClaudeEngine) GetVersionCommand() string { | |
| return "claude --version" | ||
| } | ||
|
|
||
| // 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 { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @copilot move to base type agentic engine
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moved GetOIDCConfig logic to BaseEngine. Added GetOIDCConfigWithDefault helper method that engines can use with their default configurations. (c8e796c) |
||
| // If explicit OIDC config is provided, use it | ||
| if workflowData.EngineConfig != nil && workflowData.EngineConfig.OIDC != nil && workflowData.EngineConfig.OIDC.TokenExchangeURL != "" { | ||
| return workflowData.EngineConfig.OIDC | ||
| } | ||
|
|
||
| // Return default OIDC configuration for Claude | ||
| return &OIDCConfig{ | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @copilot create this data in the Claude engine constructor
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added defaultOIDCConfig field to ClaudeEngine struct, initialized in NewClaudeEngine() constructor. (c8e796c) |
||
| 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", | ||
| } | ||
| } | ||
|
|
||
| // 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 { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @copilot Move to Odicconfig type
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added GetOAuthTokenEnvVar method to OIDCConfig type. (c8e796c) |
||
| return "ANTHROPIC_API_KEY" | ||
| } | ||
|
|
||
| // GetOAuthTokenEnvVarName returns the environment variable name for Claude's OAuth token | ||
| // This is used for OIDC-obtained tokens | ||
| func (e *ClaudeEngine) GetOAuthTokenEnvVarName() string { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @copilot love to odicconfig type
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added GetApiTokenEnvVar method to OIDCConfig type. (c8e796c) |
||
| return "CLAUDE_CODE_OAUTH_TOKEN" | ||
| } | ||
|
|
||
| // GetExecutionSteps returns the GitHub Actions steps for executing Claude | ||
| func (e *ClaudeEngine) GetExecutionSteps(workflowData *WorkflowData, logFile string) []GitHubActionStep { | ||
| // Handle custom steps if they exist in engine config | ||
| steps := InjectCustomEngineSteps(workflowData, e.convertStepToYAML) | ||
|
|
||
| // Add OIDC setup step - Claude has OIDC enabled by default | ||
| oidcConfig := e.GetOIDCConfig(workflowData) | ||
| if oidcConfig != nil { | ||
| oidcSetupStep := GenerateOIDCSetupStep(oidcConfig, e) | ||
| steps = append(steps, oidcSetupStep) | ||
| } | ||
|
|
||
| // Build claude CLI arguments based on configuration | ||
| var claudeArgs []string | ||
|
|
||
|
|
@@ -190,8 +225,13 @@ func (e *ClaudeEngine) GetExecutionSteps(workflowData *WorkflowData, logFile str | |
| // Add environment section - always include environment section for GH_AW_PROMPT | ||
| stepLines = append(stepLines, " env:") | ||
|
|
||
| // Add Anthropic API key | ||
| stepLines = append(stepLines, " ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}") | ||
| // Add authentication token - if OIDC is configured, use OAuth token from setup step | ||
| // Otherwise, use the API key secret directly | ||
| if oidcConfig != nil { | ||
| stepLines = append(stepLines, " CLAUDE_CODE_OAUTH_TOKEN: ${{ steps.setup_oidc_token.outputs.token }}") | ||
| } else { | ||
| stepLines = append(stepLines, " ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}") | ||
| } | ||
|
|
||
| // Disable telemetry, error reporting, and bug command for privacy and security | ||
| stepLines = append(stepLines, " DISABLE_TELEMETRY: \"1\"") | ||
|
|
@@ -268,6 +308,12 @@ func (e *ClaudeEngine) GetExecutionSteps(workflowData *WorkflowData, logFile str | |
| steps = append(steps, cleanupStep) | ||
| } | ||
|
|
||
| // Add OIDC revoke step - Claude has OIDC enabled by default | ||
| if oidcConfig != nil { | ||
| oidcRevokeStep := GenerateOIDCRevokeStep(oidcConfig) | ||
| steps = append(steps, oidcRevokeStep) | ||
| } | ||
|
|
||
| return steps | ||
| } | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -82,6 +82,19 @@ func (e *CodexEngine) GetVersionCommand() string { | |
| return "codex --version" | ||
| } | ||
|
|
||
| // GetOIDCConfig returns the OIDC configuration for Codex engine | ||
| func (e *CodexEngine) GetOIDCConfig(workflowData *WorkflowData) *OIDCConfig { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @copilot remove function
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed GetOIDCConfig from codex_engine.go. It now inherits the default implementation from BaseEngine which returns nil. (c8e796c) |
||
| if workflowData.EngineConfig != nil && workflowData.EngineConfig.OIDC != nil && workflowData.EngineConfig.OIDC.TokenExchangeURL != "" { | ||
| return workflowData.EngineConfig.OIDC | ||
| } | ||
| return nil | ||
| } | ||
|
|
||
| // GetTokenEnvVarName returns the environment variable name for Codex's authentication token | ||
| func (e *CodexEngine) GetTokenEnvVarName() string { | ||
| return "OPENAI_API_KEY" | ||
| } | ||
|
|
||
| // GetDeclaredOutputFiles returns the output files that Codex may produce | ||
| // Codex (written in Rust) writes logs to ~/.codex/log/codex-tui.log | ||
| func (e *CodexEngine) GetDeclaredOutputFiles() []string { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| // @ts-check | ||
| /// <reference types="@actions/github-script" /> | ||
|
|
||
| /** | ||
| * Revoke OIDC token | ||
| * | ||
| * This script revokes the app token that was obtained via OIDC token exchange. | ||
| * It only runs if a token was obtained via OIDC (not fallback). | ||
| */ | ||
|
|
||
| /** | ||
| * Main function to revoke OIDC token | ||
| */ | ||
| async function main() { | ||
| try { | ||
| // Get configuration from environment variables | ||
| const revokeUrl = process.env.GH_AW_OIDC_REVOKE_URL; | ||
| const tokenObtained = process.env.GH_AW_OIDC_TOKEN_OBTAINED; | ||
| const token = process.env.GH_AW_OIDC_TOKEN; | ||
|
|
||
| // Only revoke if we obtained a token via OIDC | ||
| if (tokenObtained !== "true") { | ||
| core.info("No OIDC token to revoke (token from fallback or not obtained)"); | ||
| return; | ||
| } | ||
|
|
||
| // If no revoke URL is configured, skip revocation | ||
| if (!revokeUrl) { | ||
| core.info("No token revoke URL configured, skipping revocation"); | ||
| return; | ||
| } | ||
|
|
||
| if (!token) { | ||
| core.warning("No token available for revocation"); | ||
| return; | ||
| } | ||
|
|
||
| core.info(`Revoking token at: ${revokeUrl}`); | ||
|
|
||
| const response = await fetch(revokeUrl, { | ||
| method: "POST", | ||
| headers: { | ||
| Authorization: `Bearer ${token}`, | ||
| }, | ||
| }); | ||
|
|
||
| if (!response.ok) { | ||
| // Log warning but don't fail the workflow | ||
| core.warning(`Token revocation failed: ${response.status} ${response.statusText}`); | ||
| return; | ||
| } | ||
|
|
||
| core.info("Token successfully revoked"); | ||
| } catch (error) { | ||
| // Log warning but don't fail the workflow for revocation errors | ||
| core.warning(`Failed to revoke token: ${error instanceof Error ? error.message : String(error)}`); | ||
| } | ||
| } | ||
|
|
||
| await main(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@copilot use dashes, not underscores