From 7bedf1f7489d46cf43687640e579eea4d43216ea Mon Sep 17 00:00:00 2001 From: Sofer Athlan-Guyot Date: Wed, 25 Feb 2026 14:48:22 +0100 Subject: [PATCH] Add OAuth token support to Anthropic authentication Adds :oauth as a third authentication option alongside :api-key and :login in agent-shell-anthropic-make-authentication. The token is passed to the Claude CLI via CLAUDE_CODE_OAUTH_TOKEN, which is the env var it expects when authenticating via `claude setup-token`. Close: #338 Co-Authored-By: Claude Sonnet 4.6 --- agent-shell-anthropic.el | 47 +++++++++++++++++++++++----- tests/agent-shell-anthropic-tests.el | 24 ++++++++++++++ 2 files changed, 63 insertions(+), 8 deletions(-) diff --git a/agent-shell-anthropic.el b/agent-shell-anthropic.el index 4942130..25bc24e 100644 --- a/agent-shell-anthropic.el +++ b/agent-shell-anthropic.el @@ -36,25 +36,31 @@ (autoload 'agent-shell-make-agent-config "agent-shell") (declare-function agent-shell--dwim "agent-shell") -(cl-defun agent-shell-anthropic-make-authentication (&key api-key login) +(cl-defun agent-shell-anthropic-make-authentication (&key api-key login oauth) "Create anthropic authentication configuration. -API-KEY is the Anthropic API key string. +API-KEY is the Anthropic API key string or a function returning one. LOGIN when non-nil indicates to use login-based authentication. +OAUTH is an OAuth token string or a function returning one. -Only one of API-KEY or LOGIN should be provided, never both." +Only one of API-KEY, LOGIN, or OAUTH should be provided, never more than one." (when (and api-key login) (error "Cannot specify both :api-key and :login - choose one")) - (unless (or api-key login) - (error "Must specify either :api-key or :login")) + (when (and oauth login) + (error "Cannot specify both :oauth and :login - choose one")) + (when (and api-key oauth) + (error "Cannot specify both :api-key and :oauth - choose one")) + (unless (or api-key login oauth) + (error "Must specify either :api-key, :login, or :oauth")) (cond + (oauth `((:oauth . ,oauth))) (api-key `((:api-key . ,api-key))) (login `((:login . t))))) (defcustom agent-shell-anthropic-authentication (agent-shell-anthropic-make-authentication :login t) "Configuration for Anthropic authentication. -For Subcription/login (default): +For subscription/login (default): (setq agent-shell-anthropic-authentication (agent-shell-anthropic-make-authentication :login t)) @@ -62,12 +68,22 @@ For Subcription/login (default): For api key: (setq agent-shell-anthropic-authentication - (agent-shell-make-anthropic-authentication :api-key \"your-key\")) + (agent-shell-anthropic-make-authentication :api-key \"your-key\")) or (setq agent-shell-anthropic-authentication - (agent-shell-make-anthropic-authentication :api-key (lambda () ... )))" + (agent-shell-anthropic-make-authentication :api-key (lambda () ... ))) + +For OAuth token: + + (setq agent-shell-anthropic-authentication + (agent-shell-anthropic-make-authentication :oauth \"your-token\")) + + or + + (setq agent-shell-anthropic-authentication + (agent-shell-anthropic-make-authentication :oauth (lambda () ... )))" :type 'alist :group 'agent-shell) @@ -159,6 +175,9 @@ additional environment variables." (agent-shell-anthropic-key)))) ((map-elt agent-shell-anthropic-authentication :login) (list "ANTHROPIC_API_KEY=")) + ((map-elt agent-shell-anthropic-authentication :oauth) + (list (format "CLAUDE_CODE_OAUTH_TOKEN=%s" + (agent-shell-anthropic-oauth-token)))) (t (error "Invalid authentication configuration"))))) (agent-shell--make-acp-client :command (car agent-shell-anthropic-claude-acp-command) @@ -179,6 +198,18 @@ additional environment variables." (t nil))) +(defun agent-shell-anthropic-oauth-token () + "Get the Anthropic OAuth token." + (cond ((stringp (map-elt agent-shell-anthropic-authentication :oauth)) + (map-elt agent-shell-anthropic-authentication :oauth)) + ((functionp (map-elt agent-shell-anthropic-authentication :oauth)) + (condition-case _err + (funcall (map-elt agent-shell-anthropic-authentication :oauth)) + (error + "OAuth token not found. Check out `agent-shell-anthropic-authentication'"))) + (t + nil))) + (defun agent-shell-anthropic--claude-code-welcome-message (config) "Return Claude Code ASCII art as per own repo using `shell-maker' CONFIG." (let ((art (agent-shell--indent-string 4 (agent-shell-anthropic--claude-code-ascii-art))) diff --git a/tests/agent-shell-anthropic-tests.el b/tests/agent-shell-anthropic-tests.el index e3900b7..33083e6 100644 --- a/tests/agent-shell-anthropic-tests.el +++ b/tests/agent-shell-anthropic-tests.el @@ -76,6 +76,30 @@ (should (member "ANTHROPIC_API_KEY=test-key" env-vars)) (should (member "NEW_VAR=new_value" env-vars)) (should (member "EXISTING_VAR=existing_value" env-vars))) + (when (buffer-live-p test-buffer) + (kill-buffer test-buffer)))) + + ;; Test with OAuth token string + (let* ((agent-shell-anthropic-authentication '(:oauth "test-oauth-token")) + (agent-shell-anthropic-claude-acp-command '("claude-agent-acp")) + (agent-shell-anthropic-claude-environment '()) + (test-buffer (get-buffer-create "*test-buffer*")) + (client (agent-shell-anthropic-make-claude-client :buffer test-buffer)) + (env-vars (map-elt client :environment-variables))) + (unwind-protect + (should (member "CLAUDE_CODE_OAUTH_TOKEN=test-oauth-token" env-vars)) + (when (buffer-live-p test-buffer) + (kill-buffer test-buffer)))) + + ;; Test with function-based OAuth token + (let* ((agent-shell-anthropic-authentication `(:oauth ,(lambda () "dynamic-oauth-token"))) + (agent-shell-anthropic-claude-acp-command '("claude-agent-acp")) + (agent-shell-anthropic-claude-environment '()) + (test-buffer (get-buffer-create "*test-buffer*")) + (client (agent-shell-anthropic-make-claude-client :buffer test-buffer)) + (env-vars (map-elt client :environment-variables))) + (unwind-protect + (should (member "CLAUDE_CODE_OAUTH_TOKEN=dynamic-oauth-token" env-vars)) (when (buffer-live-p test-buffer) (kill-buffer test-buffer))))))