mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-13 03:13:41 +08:00
fix(acp): detect keychain OAuth hijack on claude-code 'Request not allowed'
This commit is contained in:
parent
3d0797787b
commit
e5dff7acb1
@ -84,6 +84,22 @@ public class AcpRuntimeSupport {
|
||||
* and the cost of a false positive (a slightly more verbose error
|
||||
* banner) is much smaller than a false negative (user staring at a
|
||||
* raw JSON-RPC blob).
|
||||
*
|
||||
* <p>Special case: a claude-code endpoint returning {@code 403
|
||||
* "Request not allowed"} is almost always the keychain-hijack
|
||||
* scenario rather than a wrong API key. The third-party
|
||||
* {@code @zed-industries/claude-agent-acp} package wraps
|
||||
* {@code @anthropic-ai/claude-agent-sdk}, whose auth dispatcher
|
||||
* checks the macOS keychain ({@code Claude Code-credentials}) /
|
||||
* {@code ~/.claude/credentials.json} BEFORE the
|
||||
* {@code ANTHROPIC_API_KEY} env var. So a host that's done
|
||||
* {@code claude login} silently shadows whatever API key the user
|
||||
* configured in the endpoint env, and Anthropic's API rejects the
|
||||
* subscription OAuth token (first-party-only) with the very
|
||||
* specific {@code "Request not allowed"} error string. We detect
|
||||
* that exact combination and surface the keychain-clearing remedy
|
||||
* instead of the generic "set ANTHROPIC_API_KEY" hint, which
|
||||
* doesn't apply here.
|
||||
*/
|
||||
public String translateAuthError(AcpEndpointEntity endpoint, String originalMessage) {
|
||||
if (originalMessage == null) return null;
|
||||
@ -99,8 +115,32 @@ public class AcpRuntimeSupport {
|
||||
|| lower.contains("authenticate");
|
||||
if (!looksLikeAuth) return null;
|
||||
|
||||
String envVar = expectedAuthEnvVar(endpoint);
|
||||
String name = endpoint != null && endpoint.getName() != null ? endpoint.getName() : "(unknown)";
|
||||
String slug = lower(name);
|
||||
String command = endpoint != null ? lower(endpoint.getCommand()) : "";
|
||||
|
||||
// Keychain-hijack detection — must come before the generic env-
|
||||
// missing branch because both would superficially match.
|
||||
boolean keychainHijack = lower.contains("request not allowed")
|
||||
&& (slug.contains("claude") || command.contains("claude-agent-acp"));
|
||||
if (keychainHijack) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("ACP endpoint '").append(name).append("' upstream auth failed with ");
|
||||
sb.append("'Request not allowed' — almost always means the host CLI's OAuth ");
|
||||
sb.append("credentials are hijacking the SDK auth path. ");
|
||||
sb.append("The Claude Agent SDK reads ~/.claude/ / macOS keychain BEFORE the ");
|
||||
sb.append("ANTHROPIC_API_KEY env var, so the API key you configured here is ");
|
||||
sb.append("never sent — Anthropic rejects the subscription OAuth token because ");
|
||||
sb.append("third-party processes aren't allowed to use it. ");
|
||||
sb.append("To fix: ");
|
||||
sb.append("(macOS) run `claude logout`, or `security delete-generic-password ");
|
||||
sb.append("-s \"Claude Code-credentials\"`; ");
|
||||
sb.append("(Linux / Windows) delete ~/.claude/credentials.json. ");
|
||||
sb.append("Then click Test connection again. Original: ").append(originalMessage);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
String envVar = expectedAuthEnvVar(endpoint);
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("ACP endpoint '").append(name).append("' upstream auth failed. ");
|
||||
sb.append("Most likely the endpoint env has no API key. ");
|
||||
|
||||
@ -1168,7 +1168,7 @@ export default {
|
||||
openai: 'OpenAI API key (sk-…). Get one at https://platform.openai.com/api-keys',
|
||||
dashscope: 'Aliyun DashScope key — same value as MateClaw .env DASHSCOPE_API_KEY',
|
||||
google: 'Google AI Studio API key. Get one at https://aistudio.google.com/apikey',
|
||||
claudeOauth: 'Heads up: claude-code OAuth login (the one stored in ~/.claude/) does NOT work here. The third-party Zed wrapper uses the public Anthropic API, which only accepts a real API key. Get one at https://console.anthropic.com/settings/keys, or switch to qwen-code / opencode if you want to reuse a CLI login.',
|
||||
claudeOauth: 'Heads up: claude-code OAuth login (the one stored in ~/.claude/) does NOT work here, and worse — the Claude Agent SDK reads it BEFORE this env var, silently shadowing your API key. If you have ever run `claude login` on this host, clear the keychain first: (macOS) `claude logout` or `security delete-generic-password -s "Claude Code-credentials"`; (Linux/Windows) delete ~/.claude/credentials.json. Then put a real API key (sk-ant-…) above. Or switch to qwen-code / opencode to reuse a CLI login.',
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
|
||||
@ -1168,7 +1168,7 @@ export default {
|
||||
openai: 'OpenAI API key(sk-…)。申请: https://platform.openai.com/api-keys',
|
||||
dashscope: '阿里云 DashScope key — 跟 MateClaw .env 里的 DASHSCOPE_API_KEY 同一个值',
|
||||
google: 'Google AI Studio API key。申请: https://aistudio.google.com/apikey',
|
||||
claudeOauth: '注意: claude-code 的 OAuth 登录(~/.claude/ 里那个)在这里不能用。第三方 Zed wrapper 直接调 Anthropic 公开 API,只认真正的 API key。请到 https://console.anthropic.com/settings/keys 申请;或改用 qwen-code / opencode 复用各自 CLI 的本地登录态。',
|
||||
claudeOauth: '注意: claude-code 的 OAuth 登录(~/.claude/ 里那个)在这里不能用——更糟的是 Claude Agent SDK 会"优先读 OAuth、再读 API key",导致你下面填的 key 被静默忽略。如果你这台机器上跑过 `claude login`,先清钥匙串: (macOS) `claude logout` 或 `security delete-generic-password -s "Claude Code-credentials"`;(Linux/Windows) 删 ~/.claude/credentials.json。然后再回来填真正的 API key(sk-ant-…)。或者改用 qwen-code / opencode 复用各自 CLI 的本地登录态。',
|
||||
},
|
||||
},
|
||||
modal: {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user