anthropic auth proxy for multi-user claude code

Local HTTP proxy on 127.0.0.1:5051 intercepts Claude Code API requests
from sandboxed member users, injects the real OAuth token server-side,
and forwards to Anthropic. Users only see a proxy secret, never the
real credentials.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-05 03:40:14 +00:00
co-authored by Claude Opus 4.6
parent e966a71180
commit 038fd16fb2
4 changed files with 116 additions and 9 deletions
+12 -6
View File
@@ -1,8 +1,6 @@
import { join } from 'node:path';
import { existsSync } from 'node:fs';
import {
getHomeDir,
DATA_PATH,
getNativeToolsDir,
getGlobalToolsDir,
getUserToolsDir,
@@ -15,9 +13,11 @@ import { readToolDirs, parseFrontmatter as parseToolFrontmatter } from '@@/api/t
import { readSkillDirs, parseFrontmatter as parseSkillFrontmatter } from '@@/api/skills/skills';
import { buildHostToolEnv } from '@@/api/pi/pi-bridge';
import { logger } from '@@/api/pi/logger';
import { proxySecret } from '@@/api/anthropic-proxy';
import type { MessageCost, PiEvent } from '@@/api/pi/types';
const SEND_TIMEOUT_MS = 5 * 60 * 1000;
const PROXY_PORT = process.env.ANTHROPIC_PROXY_PORT ?? '5051';
// Resolve absolute path to claude binary so sudo -u can find it regardless of target user's PATH
const CLAUDE_BIN = (() => {
@@ -162,10 +162,13 @@ export async function sendClaudeCode(params: ClaudeCodeParams): Promise<ClaudeCo
const isServiceUser = shellUsername === (process.env.USER ?? '');
// For service user, keep real HOME so Claude Code finds its credentials
// For service user, keep real HOME so Claude Code finds its credentials.
// For other users, route through the local Anthropic proxy.
const env: Record<string, string> = {
...toolEnv,
...(isServiceUser ? { HOME: process.env.HOME ?? '' } : {}),
...(isServiceUser
? { HOME: process.env.HOME ?? '' }
: { ANTHROPIC_BASE_URL: `http://127.0.0.1:${PROXY_PORT}`, ANTHROPIC_API_KEY: proxySecret }),
PATH: process.env.PATH ?? '',
TERM: 'xterm-256color',
};
@@ -317,10 +320,13 @@ export async function sendClaudeCodeStreaming(params: ClaudeCodeStreamingParams)
const isServiceUser = shellUsername === (process.env.USER ?? '');
// For service user, keep real HOME so Claude Code finds its credentials
// For service user, keep real HOME so Claude Code finds its credentials.
// For other users, route through the local Anthropic proxy.
const env: Record<string, string> = {
...toolEnv,
...(isServiceUser ? { HOME: cleanEnv.HOME ?? '' } : {}),
...(isServiceUser
? { HOME: cleanEnv.HOME ?? '' }
: { ANTHROPIC_BASE_URL: `http://127.0.0.1:${PROXY_PORT}`, ANTHROPIC_API_KEY: proxySecret }),
PATH: cleanEnv.PATH ?? '',
TERM: 'xterm-256color',
};