Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/server/src/providers/claude-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import type {
// Only these vars are passed - nothing else from process.env leaks through.
const ALLOWED_ENV_VARS = [
'ANTHROPIC_API_KEY',
'ANTHROPIC_BASE_URL',
'ANTHROPIC_AUTH_TOKEN',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

While adding ANTHROPIC_AUTH_TOKEN to the allowlist is necessary, its introduction is incomplete and will cause issues with authentication checks, as several places in the codebase only check for ANTHROPIC_API_KEY.

For example, the detectInstallation method in this very file will incorrectly report that no API key is configured if only ANTHROPIC_AUTH_TOKEN is provided.

To fix this and ensure consistent behavior, all checks for authentication credentials should be updated to consider both ANTHROPIC_API_KEY and ANTHROPIC_AUTH_TOKEN.

Here are some of the locations that need to be updated:

  • detectInstallation() in apps/server/src/providers/claude-provider.ts
  • The handler in apps/server/src/routes/setup/routes/api-keys.ts
  • The handler in apps/server/src/routes/setup/routes/verify-claude-auth.ts

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ramarivera , i tried to apply this change and run dev:electron - UI shows auth errors. Did you try a non-docker run?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ooh sorry I somehow missed this message!, will take a look as soon as I can

'PATH',
'HOME',
'SHELL',
Expand Down
89 changes: 89 additions & 0 deletions apps/server/tests/unit/providers/claude-provider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ describe('claude-provider.ts', () => {
vi.clearAllMocks();
provider = new ClaudeProvider();
delete process.env.ANTHROPIC_API_KEY;
delete process.env.ANTHROPIC_BASE_URL;
delete process.env.ANTHROPIC_AUTH_TOKEN;
});

describe('getName', () => {
Expand Down Expand Up @@ -286,6 +288,93 @@ describe('claude-provider.ts', () => {
});
});

describe('environment variable passthrough', () => {
afterEach(() => {
delete process.env.ANTHROPIC_BASE_URL;
delete process.env.ANTHROPIC_AUTH_TOKEN;
});

it('should pass ANTHROPIC_BASE_URL to SDK env', async () => {
process.env.ANTHROPIC_BASE_URL = 'https://custom.example.com/v1';

vi.mocked(sdk.query).mockReturnValue(
(async function* () {
yield { type: 'text', text: 'test' };
})()
);

const generator = provider.executeQuery({
prompt: 'Test',
cwd: '/test',
});

await collectAsyncGenerator(generator);

expect(sdk.query).toHaveBeenCalledWith({
prompt: 'Test',
options: expect.objectContaining({
env: expect.objectContaining({
ANTHROPIC_BASE_URL: 'https://custom.example.com/v1',
}),
}),
});
});

it('should pass ANTHROPIC_AUTH_TOKEN to SDK env', async () => {
process.env.ANTHROPIC_AUTH_TOKEN = 'custom-auth-token';

vi.mocked(sdk.query).mockReturnValue(
(async function* () {
yield { type: 'text', text: 'test' };
})()
);

const generator = provider.executeQuery({
prompt: 'Test',
cwd: '/test',
});

await collectAsyncGenerator(generator);

expect(sdk.query).toHaveBeenCalledWith({
prompt: 'Test',
options: expect.objectContaining({
env: expect.objectContaining({
ANTHROPIC_AUTH_TOKEN: 'custom-auth-token',
}),
}),
});
});

it('should pass both custom endpoint vars together', async () => {
process.env.ANTHROPIC_BASE_URL = 'https://gateway.example.com';
process.env.ANTHROPIC_AUTH_TOKEN = 'gateway-token';

vi.mocked(sdk.query).mockReturnValue(
(async function* () {
yield { type: 'text', text: 'test' };
})()
);

const generator = provider.executeQuery({
prompt: 'Test',
cwd: '/test',
});

await collectAsyncGenerator(generator);

expect(sdk.query).toHaveBeenCalledWith({
prompt: 'Test',
options: expect.objectContaining({
env: expect.objectContaining({
ANTHROPIC_BASE_URL: 'https://gateway.example.com',
ANTHROPIC_AUTH_TOKEN: 'gateway-token',
}),
}),
});
});
});

describe('getAvailableModels', () => {
it('should return 4 Claude models', () => {
const models = provider.getAvailableModels();
Expand Down