Skip to content

Commit fa06d36

Browse files
ant-kurtstainless-app[bot]
authored andcommitted
feat(bedrock): pass client logger to AWS credential provider chain (SDK-90) (#29)
Plumb the client's logger (via loggerFor, which honors logLevel) into fromNodeProviderChain() and its clientConfig in the default provider chain resolvers, so end users (e.g. Claude Code) can debug AWS auth issues. Applied to AnthropicBedrock, AnthropicBedrockMantle, and AnthropicAws (bedrock-sdk/src/core/aws-auth.ts is a symlink to aws-sdk/src/core/auth.ts; both packages updated). The constructor deprecation-warning tests now inject a per-test logger instead of spying on console.warn: loggerFor() caches bound log functions per logger object, so once any earlier test triggers a request, a later console.warn spy is bypassed by the cached binding. Linear: SDK-90 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> :house: Remote-Dev: homespace
1 parent 0fff7fa commit fa06d36

6 files changed

Lines changed: 39 additions & 16 deletions

File tree

packages/aws-sdk/src/client.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { readEnv } from './internal/utils';
55
import { Anthropic, APIRequest, ClientOptions } from '@anthropic-ai/sdk/client';
66
export { BaseAnthropic } from '@anthropic-ai/sdk/client';
77
import { AwsCredentialIdentityProvider } from '@smithy/types';
8+
import { loggerFor } from './internal/utils/log';
89
import { loadConfig } from '@smithy/node-config-provider';
910
import { NODE_REGION_CONFIG_OPTIONS, NODE_REGION_CONFIG_FILE_OPTIONS } from '@smithy/config-resolver';
1011
import { getAuthHeaders } from './core/auth';
@@ -293,6 +294,7 @@ export class AnthropicAws extends Anthropic {
293294
awsSessionToken: this.awsSessionToken,
294295
awsProfile: this.awsProfile,
295296
providerChainResolver: this.providerChainResolver,
297+
logger: loggerFor(this),
296298
});
297299
// Signed headers take precedence: the signature must match what goes
298300
// over the wire, so it can't be overridden by other header sources.

packages/aws-sdk/src/core/auth.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import assert from 'assert';
77
import { APIConnectionError } from './error';
88
import { castToError } from '../internal/errors';
99
import { MergedRequestInit } from '../internal/types';
10+
import { Logger } from '../internal/utils/log';
1011

1112
export type AuthProps = {
1213
url: string;
@@ -18,14 +19,20 @@ export type AuthProps = {
1819
awsProfile?: string | null | undefined;
1920
fetchOptions?: MergedRequestInit | undefined;
2021
providerChainResolver?: (() => Promise<AwsCredentialIdentityProvider>) | null;
22+
logger?: Logger | undefined;
2123
};
2224

23-
const defaultProviderChainResolver = (profile?: string | null): Promise<AwsCredentialIdentityProvider> =>
25+
const defaultProviderChainResolver = (
26+
profile?: string | null,
27+
logger?: Logger | undefined,
28+
): Promise<AwsCredentialIdentityProvider> =>
2429
import('@aws-sdk/credential-providers')
2530
.then(({ fromNodeProviderChain }) =>
2631
fromNodeProviderChain({
2732
...(profile != null ? { profile } : {}),
33+
...(logger != null ? { logger } : {}),
2834
clientConfig: {
35+
...(logger != null ? { logger } : {}),
2936
requestHandler: new FetchHttpHandler({
3037
requestInit: (httpRequest) => {
3138
return {
@@ -58,7 +65,7 @@ export const getAuthHeaders = async (req: RequestInit, props: AuthProps): Promis
5865
} else {
5966
const provider = await (props.providerChainResolver ?
6067
props.providerChainResolver()
61-
: defaultProviderChainResolver(props.awsProfile));
68+
: defaultProviderChainResolver(props.awsProfile, props.logger));
6269
try {
6370
credentials = await provider();
6471
} catch (err) {

packages/bedrock-sdk/src/client.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ export class AnthropicBedrock extends BaseAnthropic {
231231
awsSessionToken: this.awsSessionToken,
232232
fetchOptions: this.fetchOptions,
233233
providerChainResolver: this.providerChainResolver,
234+
logger: loggerFor(this),
234235
});
235236
// Signed headers take precedence: the signature must match what goes
236237
// over the wire, so it can't be overridden by other header sources.

packages/bedrock-sdk/src/core/auth.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import assert from 'assert';
77
import { APIConnectionError } from './error';
88
import { castToError } from '../internal/errors';
99
import { MergedRequestInit } from '../internal/types';
10+
import { Logger } from '../internal/utils/log';
1011

1112
type AuthProps = {
1213
url: string;
@@ -16,13 +17,18 @@ type AuthProps = {
1617
awsSessionToken: string | null | undefined;
1718
fetchOptions?: MergedRequestInit | undefined;
1819
providerChainResolver?: (() => Promise<AwsCredentialIdentityProvider>) | null;
20+
logger?: Logger | undefined;
1921
};
2022

21-
const DEFAULT_PROVIDER_CHAIN_RESOLVER: () => Promise<AwsCredentialIdentityProvider> = () =>
23+
const DEFAULT_PROVIDER_CHAIN_RESOLVER: (
24+
logger?: Logger | undefined,
25+
) => Promise<AwsCredentialIdentityProvider> = (logger) =>
2226
import('@aws-sdk/credential-providers')
2327
.then(({ fromNodeProviderChain }) =>
2428
fromNodeProviderChain({
29+
...(logger != null ? { logger } : {}),
2530
clientConfig: {
31+
...(logger != null ? { logger } : {}),
2632
requestHandler: new FetchHttpHandler({
2733
requestInit: (httpRequest) => {
2834
return {
@@ -55,7 +61,7 @@ export const getAuthHeaders = async (req: RequestInit, props: AuthProps): Promis
5561
} else {
5662
const provider = await (props.providerChainResolver ?
5763
props.providerChainResolver()
58-
: DEFAULT_PROVIDER_CHAIN_RESOLVER());
64+
: DEFAULT_PROVIDER_CHAIN_RESOLVER(props.logger));
5965
try {
6066
credentials = await provider();
6167
} catch (err) {

packages/bedrock-sdk/src/mantle-client.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { AwsCredentialIdentityProvider } from '@smithy/types';
88
import { getAuthHeaders } from './core/aws-auth';
99
import type { Middleware } from './core/middleware';
1010
import { FinalRequestOptions } from './internal/request-options';
11+
import { loggerFor } from './internal/utils/log';
1112

1213
const DEFAULT_SERVICE_NAME = 'bedrock-mantle';
1314

@@ -229,6 +230,7 @@ export class AnthropicBedrockMantle extends BaseAnthropic {
229230
awsSessionToken: this.awsSessionToken,
230231
awsProfile: this.awsProfile,
231232
providerChainResolver: this.providerChainResolver,
233+
logger: loggerFor(this),
232234
});
233235
// Signed headers take precedence: the signature must match what goes
234236
// over the wire, so it can't be overridden by other header sources.

packages/bedrock-sdk/tests/client.test.ts

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -334,42 +334,47 @@ describe('middleware order (Bedrock adaptation runs inside user middleware)', ()
334334
});
335335

336336
describe('AnthropicBedrock constructor deprecation warnings', () => {
337-
let consoleWarnSpy: jest.SpyInstance;
338-
339-
beforeEach(() => {
340-
consoleWarnSpy = jest.spyOn(console, 'warn').mockImplementation();
341-
jest.resetModules();
342-
});
343-
344-
afterEach(() => {
345-
consoleWarnSpy.mockRestore();
337+
// Inject a fresh logger per test instead of spying on console.warn:
338+
// loggerFor() caches bound log functions per logger object, so a
339+
// console spy installed after the cache is populated is bypassed.
340+
const makeLogger = () => ({
341+
error: jest.fn(),
342+
warn: jest.fn(),
343+
info: jest.fn(),
344+
debug: jest.fn(),
346345
});
347346

348347
test('does not warn when both credentials are provided', () => {
348+
const logger = makeLogger();
349349
new AnthropicBedrock({
350350
awsAccessKey: 'access-key',
351351
awsSecretKey: 'secret-key',
352352
awsRegion: 'us-east-1',
353+
logger,
353354
});
354355

355-
expect(consoleWarnSpy).not.toHaveBeenCalled();
356+
expect(logger.warn).not.toHaveBeenCalled();
356357
});
357358

358359
test('does not warn when neither credential is provided', () => {
360+
const logger = makeLogger();
359361
new AnthropicBedrock({
360362
awsRegion: 'us-east-1',
363+
logger,
361364
});
362365

363-
expect(consoleWarnSpy).not.toHaveBeenCalled();
366+
expect(logger.warn).not.toHaveBeenCalled();
364367
});
365368

366369
test('warns when only one credential is provided', () => {
370+
const logger = makeLogger();
367371
new AnthropicBedrock({
368372
awsAccessKey: 'access-key',
369373
awsRegion: 'us-east-1',
374+
logger,
370375
});
371376

372-
expect(consoleWarnSpy).toHaveBeenCalledWith(
377+
expect(logger.warn).toHaveBeenCalledWith(
373378
expect.stringContaining('Passing only one of `awsAccessKey` or `awsSecretKey` is deprecated'),
374379
);
375380
});

0 commit comments

Comments
 (0)