-
Notifications
You must be signed in to change notification settings - Fork 355
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 all 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 |
|---|---|---|
|
|
@@ -12,8 +12,11 @@ import ( | |
| ) | ||
|
|
||
| // ClaudeEngine represents the Claude Code agentic engine | ||
| // ClaudeEngine represents the Claude Code engine | ||
| type ClaudeEngine struct { | ||
| BaseEngine | ||
| // defaultOIDCConfig stores the default OIDC configuration for Claude | ||
| defaultOIDCConfig *OIDCConfig | ||
| } | ||
|
|
||
| func NewClaudeEngine() *ClaudeEngine { | ||
|
|
@@ -29,6 +32,11 @@ func NewClaudeEngine() *ClaudeEngine { | |
| supportsWebFetch: true, // Claude has built-in WebFetch support | ||
| supportsWebSearch: true, // Claude has built-in WebSearch support | ||
| }, | ||
| 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", | ||
| }, | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -83,11 +91,36 @@ 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 { | ||
| return e.BaseEngine.GetOIDCConfigWithDefault(workflowData, e.defaultOIDCConfig) | ||
| } | ||
|
|
||
| // 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 +223,14 @@ 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 OR fall back to API key | ||
| // The OIDC setup step outputs the token regardless of whether it came from OIDC or API key fallback | ||
| // We need to set ANTHROPIC_API_KEY to either the OIDC token OR the secret API key | ||
| if oidcConfig != nil { | ||
| stepLines = append(stepLines, " ANTHROPIC_API_KEY: ${{ steps.setup_oidc_token.outputs.token || secrets.ANTHROPIC_API_KEY }}") | ||
| } 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 +307,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 |
|---|---|---|
| @@ -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 move to base type agentic engine
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.
Moved GetOIDCConfig logic to BaseEngine. Added GetOIDCConfigWithDefault helper method that engines can use with their default configurations. (c8e796c)