From 1f6d77954df0c3086bb6770b7b15cc4f51391701 Mon Sep 17 00:00:00 2001 From: Ralph Pina Date: Thu, 2 Jul 2026 09:12:27 -0400 Subject: [PATCH] Support keyless / environment-based auth (Workload Identity Federation) Make `claude-api-key` optional so the action can authenticate via credentials the Anthropic SDK and Claude CLI resolve from the environment -- Workload Identity Federation, ANTHROPIC_AUTH_TOKEN, Bedrock/Vertex, etc. -- instead of requiring a static API key. - action.yml: claude-api-key required:false; unset an empty ANTHROPIC_API_KEY so it does not outrank keyless auth in the SDK. - claude_api_client.py: fall through to Anthropic() (SDK auto-detect) when no key is provided, instead of raising. - github_action_audit.py: accept a static credential OR WIF env in validate_claude_available(). Providing claude-api-key behaves exactly as before; this only adds a fallback when it is absent. Co-Authored-By: Claude Opus 4.8 (1M context) --- action.yml | 18 +++++++++--------- claudecode/claude_api_client.py | 23 ++++++++++++++--------- claudecode/github_action_audit.py | 27 +++++++++++++++++++++++---- 3 files changed, 46 insertions(+), 22 deletions(-) diff --git a/action.yml b/action.yml index 0e10e6a..f83d339 100644 --- a/action.yml +++ b/action.yml @@ -24,8 +24,8 @@ inputs: default: '20' claude-api-key: - description: 'Anthropic Claude API key for security analysis' - required: true + description: 'Anthropic Claude API key for security analysis. Optional: if omitted, the action uses keyless / environment-based auth resolved by the Anthropic SDK and Claude CLI (e.g. Workload Identity Federation via ANTHROPIC_FEDERATION_RULE_ID / ANTHROPIC_ORGANIZATION_ID / ANTHROPIC_SERVICE_ACCOUNT_ID plus an OIDC identity token, or ANTHROPIC_AUTH_TOKEN). Set those in the calling workflow env when using keyless auth.' + required: false default: '' claude-model: @@ -203,14 +203,14 @@ runs: exit 0 fi - # Validate API key is provided + # Auth: a static ANTHROPIC_API_KEY is optional. If no key was provided, + # unset the (empty) variable so the Claude CLI and Anthropic SDK can + # resolve keyless credentials from the environment (e.g. Workload + # Identity Federation). An empty-but-set ANTHROPIC_API_KEY would + # otherwise take precedence over keyless auth and break it. Truly + # missing credentials are surfaced by the scan's own validation. if [ -z "$ANTHROPIC_API_KEY" ]; then - echo "::error::ANTHROPIC_API_KEY is not set. Please provide the claude-api-key input to the action." - echo "Example usage:" - echo " - uses: anthropics/claude-code-security-reviewer@main" - echo " with:" - echo " claude-api-key: \$\{{ secrets.ANTHROPIC_API_KEY }}" - exit 1 + unset ANTHROPIC_API_KEY fi # Set timeout diff --git a/claudecode/claude_api_client.py b/claudecode/claude_api_client.py index 19a6d40..cb2ca98 100644 --- a/claudecode/claude_api_client.py +++ b/claudecode/claude_api_client.py @@ -38,16 +38,21 @@ def __init__(self, self.timeout_seconds = timeout_seconds or DEFAULT_TIMEOUT_SECONDS self.max_retries = max_retries or DEFAULT_MAX_RETRIES - # Get API key from environment or parameter + # Resolve auth. An explicit key (arg or ANTHROPIC_API_KEY) is used + # directly. Otherwise, fall through to the SDK's own credential + # resolution rather than failing hard -- this enables keyless auth such + # as Workload Identity Federation (ANTHROPIC_FEDERATION_RULE_ID / + # ANTHROPIC_ORGANIZATION_ID / ANTHROPIC_SERVICE_ACCOUNT_ID plus an OIDC + # identity token) and ANTHROPIC_AUTH_TOKEN, all of which the Anthropic + # SDK auto-detects from the environment. self.api_key = api_key or os.environ.get("ANTHROPIC_API_KEY") - if not self.api_key: - raise ValueError( - "No Anthropic API key found. Please set ANTHROPIC_API_KEY environment variable " - "or provide api_key parameter." - ) - - # Initialize Anthropic client - self.client = Anthropic(api_key=self.api_key) + if self.api_key: + self.client = Anthropic(api_key=self.api_key) + else: + # No static key -- let the SDK auto-detect credentials from the + # environment. It raises a clear authentication error at call time + # if nothing is configured. + self.client = Anthropic() logger.info("Claude API client initialized successfully") def validate_api_access(self) -> Tuple[bool, str]: diff --git a/claudecode/github_action_audit.py b/claudecode/github_action_audit.py index 7e9f608..9082db7 100644 --- a/claudecode/github_action_audit.py +++ b/claudecode/github_action_audit.py @@ -323,10 +323,29 @@ def validate_claude_available(self) -> Tuple[bool, str]: ) if result.returncode == 0: - # Also check if API key is configured - api_key = os.environ.get('ANTHROPIC_API_KEY', '') - if not api_key: - return False, "ANTHROPIC_API_KEY environment variable is not set" + # Accept either a static credential or keyless / environment-based + # auth (e.g. Workload Identity Federation, ANTHROPIC_AUTH_TOKEN), + # which the Claude CLI and Anthropic SDK resolve from the + # environment. + has_static_credential = bool( + os.environ.get('ANTHROPIC_API_KEY') + or os.environ.get('ANTHROPIC_AUTH_TOKEN') + ) + has_wif = all( + os.environ.get(var) + for var in ( + 'ANTHROPIC_FEDERATION_RULE_ID', + 'ANTHROPIC_ORGANIZATION_ID', + 'ANTHROPIC_SERVICE_ACCOUNT_ID', + ) + ) + if not (has_static_credential or has_wif): + return False, ( + "No Anthropic credentials configured. Set ANTHROPIC_API_KEY, " + "or configure Workload Identity Federation " + "(ANTHROPIC_FEDERATION_RULE_ID / ANTHROPIC_ORGANIZATION_ID / " + "ANTHROPIC_SERVICE_ACCOUNT_ID)." + ) return True, "" else: error_msg = f"Claude Code returned exit code {result.returncode}"