Skip to content

Commit 4960a92

Browse files
authored
fix(credential-providers): use modified region resolver for inner STS clients (#7456)
1 parent 6865eb6 commit 4960a92

38 files changed

+2029
-255
lines changed

clients/client-sts/src/defaultStsRoleAssumers.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Please do not touch this file. It's generated from template in:
33
// https://github.com/aws/aws-sdk-js-v3/blob/main/codegen/smithy-aws-typescript-codegen/src/main/resources/software/amazon/smithy/aws/typescript/codegen/sts-client-defaultStsRoleAssumers.ts
44
import { setCredentialFeature } from "@aws-sdk/core/client";
5+
import { stsRegionDefaultResolver } from "@aws-sdk/region-config-resolver";
56
import type { CredentialProviderOptions } from "@aws-sdk/types";
67
import { AwsCredentialIdentity, Logger, Provider } from "@smithy/types";
78

@@ -28,8 +29,6 @@ export type RoleAssumer = (
2829
params: AssumeRoleCommandInput
2930
) => Promise<AwsCredentialIdentity>;
3031

31-
const ASSUME_ROLE_DEFAULT_REGION = "us-east-1";
32-
3332
interface AssumedRoleUser {
3433
/**
3534
* The ARN of the temporary security credentials that are returned from the AssumeRole action.
@@ -63,19 +62,21 @@ const getAccountIdFromAssumedRoleUser = (assumedRoleUser?: AssumedRoleUser) => {
6362
const resolveRegion = async (
6463
_region: string | Provider<string> | undefined,
6564
_parentRegion: string | Provider<string> | undefined,
66-
credentialProviderLogger?: Logger
65+
credentialProviderLogger?: Logger,
66+
loaderConfig: Parameters<typeof stsRegionDefaultResolver>[0] = {}
6767
): Promise<string> => {
6868
const region: string | undefined = typeof _region === "function" ? await _region() : _region;
6969
const parentRegion: string | undefined = typeof _parentRegion === "function" ? await _parentRegion() : _parentRegion;
70+
const stsDefaultRegion = await stsRegionDefaultResolver(loaderConfig)();
7071

7172
credentialProviderLogger?.debug?.(
7273
"@aws-sdk/client-sts::resolveRegion",
7374
"accepting first of:",
74-
`${region} (provider)`,
75-
`${parentRegion} (parent client)`,
76-
`${ASSUME_ROLE_DEFAULT_REGION} (STS default)`
75+
`${region} (credential provider clientConfig)`,
76+
`${parentRegion} (contextual client)`,
77+
`${stsDefaultRegion} (STS default: AWS_REGION, profile region, or us-east-1)`
7778
);
78-
return region ?? parentRegion ?? ASSUME_ROLE_DEFAULT_REGION;
79+
return region ?? parentRegion ?? stsDefaultRegion;
7980
};
8081

8182
/**
@@ -101,7 +102,11 @@ export const getDefaultRoleAssumer = (
101102
const resolvedRegion = await resolveRegion(
102103
region,
103104
stsOptions?.parentClientConfig?.region,
104-
credentialProviderLogger
105+
credentialProviderLogger,
106+
{
107+
logger,
108+
profile,
109+
}
105110
);
106111
const isCompatibleRequestHandler = !isH2(requestHandler);
107112

@@ -164,7 +169,11 @@ export const getDefaultRoleAssumerWithWebIdentity = (
164169
const resolvedRegion = await resolveRegion(
165170
region,
166171
stsOptions?.parentClientConfig?.region,
167-
credentialProviderLogger
172+
credentialProviderLogger,
173+
{
174+
logger,
175+
profile,
176+
}
168177
);
169178
const isCompatibleRequestHandler = !isH2(requestHandler);
170179

clients/client-sts/test/defaultRoleAssumers.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ describe("getDefaultRoleAssumer", () => {
139139
requestHandler: handler,
140140
parentClientConfig: {
141141
region: "some-other-region",
142-
logger: null,
142+
logger: null as any,
143143
requestHandler: null,
144144
},
145145
});

codegen/smithy-aws-typescript-codegen/src/main/resources/software/amazon/smithy/aws/typescript/codegen/sts-client-defaultRoleAssumers.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ describe("getDefaultRoleAssumer", () => {
137137
requestHandler: handler,
138138
parentClientConfig: {
139139
region: "some-other-region",
140-
logger: null,
140+
logger: null as any,
141141
requestHandler: null,
142142
},
143143
});

codegen/smithy-aws-typescript-codegen/src/main/resources/software/amazon/smithy/aws/typescript/codegen/sts-client-defaultStsRoleAssumers.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { setCredentialFeature } from "@aws-sdk/core/client";
2+
import { stsRegionDefaultResolver } from "@aws-sdk/region-config-resolver";
23
import type { CredentialProviderOptions } from "@aws-sdk/types";
34
import { AwsCredentialIdentity, Logger, Provider } from "@smithy/types";
45

@@ -25,8 +26,6 @@ export type RoleAssumer = (
2526
params: AssumeRoleCommandInput
2627
) => Promise<AwsCredentialIdentity>;
2728

28-
const ASSUME_ROLE_DEFAULT_REGION = "us-east-1";
29-
3029
interface AssumedRoleUser {
3130
/**
3231
* The ARN of the temporary security credentials that are returned from the AssumeRole action.
@@ -60,19 +59,21 @@ const getAccountIdFromAssumedRoleUser = (assumedRoleUser?: AssumedRoleUser) => {
6059
const resolveRegion = async (
6160
_region: string | Provider<string> | undefined,
6261
_parentRegion: string | Provider<string> | undefined,
63-
credentialProviderLogger?: Logger
62+
credentialProviderLogger?: Logger,
63+
loaderConfig: Parameters<typeof stsRegionDefaultResolver>[0] = {}
6464
): Promise<string> => {
6565
const region: string | undefined = typeof _region === "function" ? await _region() : _region;
6666
const parentRegion: string | undefined = typeof _parentRegion === "function" ? await _parentRegion() : _parentRegion;
67+
const stsDefaultRegion = await stsRegionDefaultResolver(loaderConfig)();
6768

6869
credentialProviderLogger?.debug?.(
6970
"@aws-sdk/client-sts::resolveRegion",
7071
"accepting first of:",
71-
`${region} (provider)`,
72-
`${parentRegion} (parent client)`,
73-
`${ASSUME_ROLE_DEFAULT_REGION} (STS default)`
72+
`${region} (credential provider clientConfig)`,
73+
`${parentRegion} (contextual client)`,
74+
`${stsDefaultRegion} (STS default: AWS_REGION, profile region, or us-east-1)`
7475
);
75-
return region ?? parentRegion ?? ASSUME_ROLE_DEFAULT_REGION;
76+
return region ?? parentRegion ?? stsDefaultRegion;
7677
};
7778

7879
/**
@@ -98,7 +99,11 @@ export const getDefaultRoleAssumer = (
9899
const resolvedRegion = await resolveRegion(
99100
region,
100101
stsOptions?.parentClientConfig?.region,
101-
credentialProviderLogger
102+
credentialProviderLogger,
103+
{
104+
logger,
105+
profile,
106+
}
102107
);
103108
const isCompatibleRequestHandler = !isH2(requestHandler);
104109

@@ -161,7 +166,11 @@ export const getDefaultRoleAssumerWithWebIdentity = (
161166
const resolvedRegion = await resolveRegion(
162167
region,
163168
stsOptions?.parentClientConfig?.region,
164-
credentialProviderLogger
169+
credentialProviderLogger,
170+
{
171+
logger,
172+
profile,
173+
}
165174
);
166175
const isCompatibleRequestHandler = !isH2(requestHandler);
167176

packages/credential-provider-ini/src/fromIni.integ.spec.ts

Lines changed: 109 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,33 @@
11
import { STS } from "@aws-sdk/client-sts";
22
import { HttpRequest, HttpResponse } from "@smithy/protocol-http";
3-
import { SourceProfileInit } from "@smithy/shared-ini-file-loader";
3+
import { externalDataInterceptor } from "@smithy/shared-ini-file-loader";
44
import type { NodeHttpHandlerOptions, ParsedIniData } from "@smithy/types";
5+
import { homedir } from "node:os";
6+
import { join } from "node:path";
57
import { PassThrough } from "node:stream";
6-
import { afterEach, beforeEach, describe, expect, test as it, vi } from "vitest";
8+
import { afterEach, beforeEach, describe, expect, test as it } from "vitest";
79

810
import { fromIni } from "./fromIni";
911

1012
let iniProfileData: ParsedIniData = null as any;
11-
vi.mock("@smithy/shared-ini-file-loader", async () => {
12-
const actual: any = await vi.importActual("@smithy/shared-ini-file-loader");
13-
const pkg = {
14-
...actual,
15-
async loadSsoSessionData() {
16-
return Object.entries(iniProfileData)
17-
.filter(([key]) => key.startsWith("sso-session."))
18-
.reduce(
19-
(acc, [key, value]) => ({
20-
...acc,
21-
[key.split("sso-session.")[1]]: value,
22-
}),
23-
{}
24-
);
25-
},
26-
async parseKnownFiles(init: SourceProfileInit): Promise<ParsedIniData> {
27-
return iniProfileData;
28-
},
29-
async getSSOTokenFromFile() {
30-
return {
31-
accessToken: "mock_sso_token",
32-
expiresAt: "3000-01-01T00:00:00.000Z",
33-
};
34-
},
35-
};
36-
return {
37-
...pkg,
38-
default: pkg,
39-
};
40-
});
13+
14+
function setIniProfileData(data: ParsedIniData) {
15+
iniProfileData = data;
16+
let buffer = "";
17+
for (const profile in data) {
18+
if (profile.startsWith("sso-session.")) {
19+
buffer += `[sso-session ${profile.split("sso-session.")[1]}]\n`;
20+
} else {
21+
buffer += `[profile ${profile}]\n`;
22+
}
23+
for (const [k, v] of Object.entries(data[profile])) {
24+
buffer += `${k} = ${v}\n`;
25+
}
26+
buffer += "\n";
27+
}
28+
const dir = join(homedir(), ".aws");
29+
externalDataInterceptor.interceptFile(join(dir, "config"), buffer);
30+
}
4131

4232
class MockNodeHttpHandler {
4333
static create(instanceOrOptions?: any) {
@@ -46,6 +36,7 @@ class MockNodeHttpHandler {
4636
}
4737
return new MockNodeHttpHandler();
4838
}
39+
4940
async handle(request: HttpRequest) {
5041
const body = new PassThrough({});
5142

@@ -125,7 +116,9 @@ class MockNodeHttpHandler {
125116
}),
126117
};
127118
}
119+
128120
updateHttpClientConfig(key: keyof NodeHttpHandlerOptions, value: NodeHttpHandlerOptions[typeof key]): void {}
121+
129122
httpHandlerConfigs(): NodeHttpHandlerOptions {
130123
return null as any;
131124
}
@@ -136,22 +129,20 @@ describe("fromIni region search order", () => {
136129
process.env.AWS_PROFILE = "default";
137130
iniProfileData = {
138131
default: {
139-
region: "us-west-2",
140132
output: "json",
133+
region: "us-stsar-1",
134+
role_arn: "ROLE_ARN",
135+
role_session_name: "ROLE_SESSION_NAME",
136+
external_id: "EXTERNAL_ID",
137+
source_profile: "assume",
138+
},
139+
assume: {
140+
region: "us-stsar-1",
141+
aws_access_key_id: "ASSUME_STATIC_ACCESS_KEY",
142+
aws_secret_access_key: "ASSUME_STATIC_SECRET_KEY",
141143
},
142144
};
143-
iniProfileData.assume = {
144-
region: "us-stsar-1",
145-
aws_access_key_id: "ASSUME_STATIC_ACCESS_KEY",
146-
aws_secret_access_key: "ASSUME_STATIC_SECRET_KEY",
147-
};
148-
Object.assign(iniProfileData.default, {
149-
region: "us-stsar-1",
150-
role_arn: "ROLE_ARN",
151-
role_session_name: "ROLE_SESSION_NAME",
152-
external_id: "EXTERNAL_ID",
153-
source_profile: "assume",
154-
});
145+
setIniProfileData(iniProfileData);
155146
});
156147

157148
afterEach(() => {
@@ -201,6 +192,7 @@ describe("fromIni region search order", () => {
201192

202193
it("should use 3rd priority for the caller client", async () => {
203194
delete iniProfileData.default.region;
195+
setIniProfileData(iniProfileData);
204196

205197
const sts = new STS({
206198
requestHandler: new MockNodeHttpHandler(),
@@ -221,8 +213,78 @@ describe("fromIni region search order", () => {
221213
});
222214
});
223215

224-
it("should use 4th priority for the default partition's default region", async () => {
225-
delete iniProfileData.default.region;
216+
it("should use 4th priority for the config file region", async () => {
217+
const credentialsData = await fromIni({
218+
clientConfig: {
219+
requestHandler: new MockNodeHttpHandler(),
220+
},
221+
})();
222+
223+
const sts = new STS({
224+
requestHandler: new MockNodeHttpHandler(),
225+
credentials: credentialsData,
226+
});
227+
228+
await sts.getCallerIdentity({});
229+
const credentials = await sts.config.credentials();
230+
expect(credentials).toMatchObject({
231+
accessKeyId: "STS_AR_ACCESS_KEY_ID",
232+
secretAccessKey: "STS_AR_SECRET_ACCESS_KEY",
233+
sessionToken: "STS_AR_SESSION_TOKEN_us-stsar-1",
234+
});
235+
});
236+
237+
it("should use 5th priority for the AWS_REGION value", async () => {
238+
process.env.AWS_REGION = "ap-northeast-1";
239+
iniProfileData = {
240+
default: {
241+
role_arn: "ROLE_ARN",
242+
role_session_name: "ROLE_SESSION_NAME",
243+
external_id: "EXTERNAL_ID",
244+
source_profile: "assume",
245+
},
246+
assume: {
247+
aws_access_key_id: "ASSUME_STATIC_ACCESS_KEY",
248+
aws_secret_access_key: "ASSUME_STATIC_SECRET_KEY",
249+
},
250+
};
251+
setIniProfileData(iniProfileData);
252+
253+
const credentialsData = await fromIni({
254+
clientConfig: {
255+
requestHandler: new MockNodeHttpHandler(),
256+
},
257+
})();
258+
259+
const sts = new STS({
260+
requestHandler: new MockNodeHttpHandler(),
261+
credentials: credentialsData,
262+
});
263+
264+
await sts.getCallerIdentity({});
265+
const credentials = await sts.config.credentials();
266+
expect(credentials).toMatchObject({
267+
accessKeyId: "STS_AR_ACCESS_KEY_ID",
268+
secretAccessKey: "STS_AR_SECRET_ACCESS_KEY",
269+
sessionToken: "STS_AR_SESSION_TOKEN_ap-northeast-1",
270+
});
271+
});
272+
273+
it("should use 6th priority for the default partition's default region", async () => {
274+
delete process.env.AWS_REGION;
275+
iniProfileData = {
276+
default: {
277+
role_arn: "ROLE_ARN",
278+
role_session_name: "ROLE_SESSION_NAME",
279+
external_id: "EXTERNAL_ID",
280+
source_profile: "assume",
281+
},
282+
assume: {
283+
aws_access_key_id: "ASSUME_STATIC_ACCESS_KEY",
284+
aws_secret_access_key: "ASSUME_STATIC_SECRET_KEY",
285+
},
286+
};
287+
setIniProfileData(iniProfileData);
226288

227289
const credentialsData = await fromIni({
228290
clientConfig: {

packages/credential-provider-ini/src/resolveSsoCredentials.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ describe(resolveSsoCredentials.name, () => {
6464
secretAccessKey: "mockSecretAccessKey",
6565
};
6666
const requestHandler = vi.fn();
67-
const logger = vi.fn();
67+
const logger: any = vi.fn();
6868

6969
vi.mocked(fromSSO).mockReturnValue(() => Promise.resolve(mockCreds));
7070

packages/credential-provider-node/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"build:types": "tsc -p tsconfig.types.json",
1616
"build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4",
1717
"clean": "rimraf ./dist-* && rimraf *.tsbuildinfo",
18-
"test": "yarn g:vitest run",
18+
"test": "yarn g:vitest run --reporter verbose",
1919
"test:watch": "yarn g:vitest watch",
2020
"test:integration": "yarn g:vitest run -c vitest.config.integ.mts",
2121
"test:integration:watch": "yarn g:vitest watch -c vitest.config.integ.mts"

packages/credential-provider-node/src/defaultProvider.spec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ describe(defaultProvider.name, () => {
2626
};
2727

2828
const credentials = () => {
29-
throw new CredentialsProviderError("test", true);
29+
throw new CredentialsProviderError("test", {
30+
tryNextLink: true,
31+
});
3032
};
3133

3234
const finalCredentials = () => {

0 commit comments

Comments
 (0)