Skip to content

Commit 7fb4e26

Browse files
committed
feat(credential-providers): make credential providers aware of contextual client region
1 parent afface0 commit 7fb4e26

File tree

15 files changed

+231
-62
lines changed

15 files changed

+231
-62
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"copy-models": "node ./scripts/copy-models",
2525
"extract:docs": "node ./scripts/extract-docs/index.js",
2626
"g:vitest": "cd $INIT_CWD && vitest",
27+
"g:jest": "cd $INIT_CWD && jest",
2728
"generate-clients": "node ./scripts/generate-clients",
2829
"generate:clients:generic": "node ./scripts/generate-clients/generic",
2930
"generate:defaults-mode-provider": "./scripts/generate-defaults-mode-provider/index.js",

packages/core/src/submodules/httpAuthSchemes/aws_sdk/resolveAwsSdkSigV4Config.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -106,29 +106,33 @@ export const resolveAwsSdkSigV4Config = <T>(
106106
): T & AwsSdkSigV4AuthResolvedConfig => {
107107
let isUserSupplied = false;
108108
// Normalize credentials
109-
let normalizedCreds: AwsCredentialIdentityProvider | undefined;
109+
let credentialsProvider: AwsCredentialIdentityProvider | undefined;
110110
if (config.credentials) {
111111
isUserSupplied = true;
112-
normalizedCreds = memoizeIdentityProvider(config.credentials, isIdentityExpired, doesIdentityRequireRefresh);
112+
credentialsProvider = memoizeIdentityProvider(config.credentials, isIdentityExpired, doesIdentityRequireRefresh);
113113
}
114-
if (!normalizedCreds) {
114+
if (!credentialsProvider) {
115115
// credentialDefaultProvider should always be populated, but in case
116116
// it isn't, set a default identity provider that throws an error
117117
if (config.credentialDefaultProvider) {
118-
normalizedCreds = normalizeProvider(
118+
credentialsProvider = normalizeProvider(
119119
config.credentialDefaultProvider(
120120
Object.assign({}, config as any, {
121121
parentClientConfig: config,
122122
})
123123
)
124124
);
125125
} else {
126-
normalizedCreds = async () => {
126+
credentialsProvider = async () => {
127127
throw new Error("`credentials` is missing");
128128
};
129129
}
130130
}
131131

132+
const contextBoundCredentialsProvider = async () => {
133+
return credentialsProvider!({ contextClientConfig: config });
134+
};
135+
132136
// Populate sigv4 arguments
133137
const {
134138
// Default for signingEscapePath
@@ -170,7 +174,7 @@ export const resolveAwsSdkSigV4Config = <T>(
170174

171175
const params: SignatureV4Init & SignatureV4CryptoInit = {
172176
...config,
173-
credentials: normalizedCreds!,
177+
credentials: contextBoundCredentialsProvider,
174178
region: config.signingRegion,
175179
service: config.signingName,
176180
sha256,
@@ -206,7 +210,7 @@ export const resolveAwsSdkSigV4Config = <T>(
206210

207211
const params: SignatureV4Init & SignatureV4CryptoInit = {
208212
...config,
209-
credentials: normalizedCreds!,
213+
credentials: contextBoundCredentialsProvider,
210214
region: config.signingRegion,
211215
service: config.signingName,
212216
sha256,
@@ -224,10 +228,10 @@ export const resolveAwsSdkSigV4Config = <T>(
224228
signingEscapePath,
225229
credentials: isUserSupplied
226230
? async () =>
227-
normalizedCreds!().then((creds: AttributedAwsCredentialIdentity) =>
231+
contextBoundCredentialsProvider!().then((creds: AttributedAwsCredentialIdentity) =>
228232
setCredentialFeature(creds, "CREDENTIALS_CODE", "e")
229233
)
230-
: normalizedCreds!,
234+
: contextBoundCredentialsProvider!,
231235
signer,
232236
};
233237
};

packages/credential-provider-cognito-identity/src/fromCognitoIdentity.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import type { CredentialProviderOptions } from "@aws-sdk/types";
1+
import type { AwsIdentityProperties, CredentialProviderOptions, RegionalIdentityProvider } from "@aws-sdk/types";
22
import { CredentialsProviderError } from "@smithy/property-provider";
3-
import { AwsCredentialIdentity, Logger, Provider } from "@smithy/types";
3+
import type { AwsCredentialIdentity, Logger } from "@smithy/types";
44

55
import { CognitoProviderParameters } from "./CognitoProviderParameters";
66
import { resolveLogins } from "./resolveLogins";
@@ -18,7 +18,7 @@ export interface CognitoIdentityCredentials extends AwsCredentialIdentity {
1818
/**
1919
* @internal
2020
*/
21-
export type CognitoIdentityCredentialProvider = Provider<CognitoIdentityCredentials>;
21+
export type CognitoIdentityCredentialProvider = RegionalIdentityProvider<CognitoIdentityCredentials>;
2222

2323
/**
2424
* @internal
@@ -29,7 +29,7 @@ export type CognitoIdentityCredentialProvider = Provider<CognitoIdentityCredenti
2929
* Results from this function call are not cached internally.
3030
*/
3131
export function fromCognitoIdentity(parameters: FromCognitoIdentityParameters): CognitoIdentityCredentialProvider {
32-
return async (): Promise<CognitoIdentityCredentials> => {
32+
return async (awsIdentityProperties?: AwsIdentityProperties): Promise<CognitoIdentityCredentials> => {
3333
parameters.logger?.debug("@aws-sdk/credential-provider-cognito-identity - fromCognitoIdentity");
3434
const { GetCredentialsForIdentityCommand, CognitoIdentityClient } = await import("./loadCognitoIdentity");
3535

@@ -44,7 +44,10 @@ export function fromCognitoIdentity(parameters: FromCognitoIdentityParameters):
4444
parameters.client ??
4545
new CognitoIdentityClient(
4646
Object.assign({}, parameters.clientConfig ?? {}, {
47-
region: parameters.clientConfig?.region ?? parameters.parentClientConfig?.region,
47+
region:
48+
parameters.clientConfig?.region ??
49+
parameters.parentClientConfig?.region ??
50+
awsIdentityProperties?.contextClientConfig?.region,
4851
})
4952
)
5053
).send(

packages/credential-provider-cognito-identity/src/fromCognitoIdentityPool.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { CredentialProviderOptions } from "@aws-sdk/types";
1+
import type { AwsIdentityProperties, CredentialProviderOptions } from "@aws-sdk/types";
22
import { CredentialsProviderError } from "@smithy/property-provider";
33
import { Logger } from "@smithy/types";
44

@@ -35,12 +35,15 @@ export function fromCognitoIdentityPool({
3535
? `aws:cognito-identity-credentials:${identityPoolId}:${userIdentifier}`
3636
: undefined;
3737

38-
let provider: CognitoIdentityCredentialProvider = async () => {
38+
let provider: CognitoIdentityCredentialProvider = async (awsIdentityProperties?: AwsIdentityProperties) => {
3939
const { GetIdCommand, CognitoIdentityClient } = await import("./loadCognitoIdentity");
4040
const _client =
4141
client ??
4242
new CognitoIdentityClient(
43-
Object.assign({}, clientConfig ?? {}, { region: clientConfig?.region ?? parentClientConfig?.region })
43+
Object.assign({}, clientConfig ?? {}, {
44+
region:
45+
clientConfig?.region ?? parentClientConfig?.region ?? awsIdentityProperties?.contextClientConfig?.region,
46+
})
4447
);
4548

4649
let identityId: string | undefined = (cacheKey && (await cache.getItem(cacheKey))) as string | undefined;

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

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import type { AssumeRoleWithWebIdentityParams } from "@aws-sdk/credential-provider-web-identity";
22
import type { CredentialProviderOptions } from "@aws-sdk/types";
3+
import type { RegionalAwsCredentialIdentityProvider } from "@aws-sdk/types";
34
import { getProfileName, parseKnownFiles, SourceProfileInit } from "@smithy/shared-ini-file-loader";
4-
import type { AwsCredentialIdentity, AwsCredentialIdentityProvider, Pluggable } from "@smithy/types";
5+
import type { AwsCredentialIdentity, Pluggable } from "@smithy/types";
56

67
import { AssumeRoleParams } from "./resolveAssumeRoleCredentials";
78
import { resolveProfileData } from "./resolveProfileData";
@@ -55,8 +56,15 @@ export interface FromIniInit extends SourceProfileInit, CredentialProviderOption
5556
* role assumption and multi-factor authentication.
5657
*/
5758
export const fromIni =
58-
(init: FromIniInit = {}): AwsCredentialIdentityProvider =>
59-
async () => {
59+
(_init: FromIniInit = {}): RegionalAwsCredentialIdentityProvider =>
60+
async (props = {}) => {
61+
const init: FromIniInit = {
62+
..._init,
63+
parentClientConfig: {
64+
region: props.contextClientConfig?.region,
65+
..._init.parentClientConfig,
66+
},
67+
};
6068
init.logger?.debug("@aws-sdk/credential-provider-ini - fromIni");
6169
const profiles = await parseKnownFiles(init);
6270
return resolveProfileData(getProfileName(init), profiles, init);

packages/credential-provider-node/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"build:types:downlevel": "downlevel-dts dist-types dist-types/ts3.4",
1717
"clean": "rimraf ./dist-* && rimraf *.tsbuildinfo",
1818
"test": "yarn g:vitest run",
19-
"test:integration": "jest -c jest.config.integ.js",
19+
"test:integration": "yarn g:jest -c jest.config.integ.js",
2020
"test:watch": "yarn g:vitest watch"
2121
},
2222
"keywords": [

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

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { STS } from "@aws-sdk/client-sts";
22
import * as credentialProviderHttp from "@aws-sdk/credential-provider-http";
3+
import { fromIni } from "@aws-sdk/credential-providers";
34
import { HttpResponse } from "@smithy/protocol-http";
45
import type { SourceProfileInit } from "@smithy/shared-ini-file-loader";
56
import type { HttpRequest, NodeHttpHandlerOptions, ParsedIniData } from "@smithy/types";
@@ -67,6 +68,8 @@ jest.mock("@smithy/node-http-handler", () => {
6768
assumeRoleArns.push(request.body.match(/RoleArn=(.*?)&/)?.[1]);
6869
}
6970

71+
const region = (request.hostname.match(/sts\.(.*?)\./) || [, "unknown"])[1];
72+
7073
if (request.headers.Authorization === "container-authorization") {
7174
body.write(
7275
JSON.stringify({
@@ -94,7 +97,7 @@ jest.mock("@smithy/node-http-handler", () => {
9497
<Credentials>
9598
<AccessKeyId>STS_ARWI_ACCESS_KEY_ID</AccessKeyId>
9699
<SecretAccessKey>STS_ARWI_SECRET_ACCESS_KEY</SecretAccessKey>
97-
<SessionToken>STS_ARWI_SESSION_TOKEN</SessionToken>
100+
<SessionToken>STS_ARWI_SESSION_TOKEN_${region}</SessionToken>
98101
<Expiration>3000-01-01T00:00:00.000Z</Expiration>
99102
</Credentials>
100103
</AssumeRoleWithWebIdentityResult>
@@ -109,7 +112,7 @@ jest.mock("@smithy/node-http-handler", () => {
109112
<Credentials>
110113
<AccessKeyId>STS_AR_ACCESS_KEY_ID</AccessKeyId>
111114
<SecretAccessKey>STS_AR_SECRET_ACCESS_KEY</SecretAccessKey>
112-
<SessionToken>STS_AR_SESSION_TOKEN</SessionToken>
115+
<SessionToken>STS_AR_SESSION_TOKEN_${region}</SessionToken>
113116
<Expiration>3000-01-01T00:00:00.000Z</Expiration>
114117
</Credentials>
115118
</AssumeRoleResult>
@@ -379,7 +382,7 @@ describe("credential-provider-node integration test", () => {
379382
expect(credentials).toEqual({
380383
accessKeyId: "STS_AR_ACCESS_KEY_ID",
381384
secretAccessKey: "STS_AR_SECRET_ACCESS_KEY",
382-
sessionToken: "STS_AR_SESSION_TOKEN",
385+
sessionToken: "STS_AR_SESSION_TOKEN_us-west-2",
383386
expiration: new Date("3000-01-01T00:00:00.000Z"),
384387
$source: {
385388
CREDENTIALS_PROFILE_SOURCE_PROFILE: "o",
@@ -409,7 +412,7 @@ describe("credential-provider-node integration test", () => {
409412
expect(credentials).toEqual({
410413
accessKeyId: "STS_AR_ACCESS_KEY_ID",
411414
secretAccessKey: "STS_AR_SECRET_ACCESS_KEY",
412-
sessionToken: "STS_AR_SESSION_TOKEN",
415+
sessionToken: "STS_AR_SESSION_TOKEN_eu-west-1",
413416
expiration: new Date("3000-01-01T00:00:00.000Z"),
414417
$source: {
415418
CREDENTIALS_PROFILE_SOURCE_PROFILE: "o",
@@ -439,9 +442,41 @@ describe("credential-provider-node integration test", () => {
439442
expect(credentials).toEqual({
440443
accessKeyId: "STS_AR_ACCESS_KEY_ID",
441444
secretAccessKey: "STS_AR_SECRET_ACCESS_KEY",
442-
sessionToken: "STS_AR_SESSION_TOKEN",
445+
sessionToken: "STS_AR_SESSION_TOKEN_us-gov-stsar-1",
446+
expiration: new Date("3000-01-01T00:00:00.000Z"),
447+
$source: {
448+
CREDENTIALS_PROFILE_SOURCE_PROFILE: "o",
449+
CREDENTIALS_STS_ASSUME_ROLE: "i",
450+
},
451+
});
452+
});
453+
454+
it("should use the context client's region for STS even if initialized separately in a code-level provider", async () => {
455+
sts = new STS({
456+
region: "eu-west-1",
457+
credentials: fromIni(),
458+
});
459+
iniProfileData.assume = {
460+
region: "eu-west-1",
461+
aws_access_key_id: "ASSUME_STATIC_ACCESS_KEY",
462+
aws_secret_access_key: "ASSUME_STATIC_SECRET_KEY",
463+
};
464+
Object.assign(iniProfileData.default, {
465+
region: "eu-west-1",
466+
role_arn: "ROLE_ARN",
467+
role_session_name: "ROLE_SESSION_NAME",
468+
external_id: "EXTERNAL_ID",
469+
source_profile: "assume",
470+
});
471+
await sts.getCallerIdentity({});
472+
const credentials = await sts.config.credentials();
473+
expect(credentials).toEqual({
474+
accessKeyId: "STS_AR_ACCESS_KEY_ID",
475+
secretAccessKey: "STS_AR_SECRET_ACCESS_KEY",
476+
sessionToken: "STS_AR_SESSION_TOKEN_eu-west-1",
443477
expiration: new Date("3000-01-01T00:00:00.000Z"),
444478
$source: {
479+
CREDENTIALS_CODE: "e",
445480
CREDENTIALS_PROFILE_SOURCE_PROFILE: "o",
446481
CREDENTIALS_STS_ASSUME_ROLE: "i",
447482
},
@@ -458,7 +493,7 @@ describe("credential-provider-node integration test", () => {
458493
expect(credentials).toEqual({
459494
accessKeyId: "STS_ARWI_ACCESS_KEY_ID",
460495
secretAccessKey: "STS_ARWI_SECRET_ACCESS_KEY",
461-
sessionToken: "STS_ARWI_SESSION_TOKEN",
496+
sessionToken: "STS_ARWI_SESSION_TOKEN_us-west-2",
462497
expiration: new Date("3000-01-01T00:00:00.000Z"),
463498
$source: {
464499
CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN: "q",
@@ -484,7 +519,7 @@ describe("credential-provider-node integration test", () => {
484519
expect(credentials).toEqual({
485520
accessKeyId: "STS_ARWI_ACCESS_KEY_ID",
486521
secretAccessKey: "STS_ARWI_SECRET_ACCESS_KEY",
487-
sessionToken: "STS_ARWI_SESSION_TOKEN",
522+
sessionToken: "STS_ARWI_SESSION_TOKEN_us-gov-sts-1",
488523
expiration: new Date("3000-01-01T00:00:00.000Z"),
489524
$source: {
490525
CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN: "q",
@@ -562,7 +597,7 @@ describe("credential-provider-node integration test", () => {
562597
expect(credentials).toEqual({
563598
accessKeyId: "STS_AR_ACCESS_KEY_ID",
564599
secretAccessKey: "STS_AR_SECRET_ACCESS_KEY",
565-
sessionToken: "STS_AR_SESSION_TOKEN",
600+
sessionToken: "STS_AR_SESSION_TOKEN_us-west-2",
566601
expiration: new Date("3000-01-01T00:00:00.000Z"),
567602
$source: {
568603
CREDENTIALS_CODE: "e",
@@ -605,7 +640,7 @@ describe("credential-provider-node integration test", () => {
605640
expect(credentials).toEqual({
606641
accessKeyId: "STS_AR_ACCESS_KEY_ID",
607642
secretAccessKey: "STS_AR_SECRET_ACCESS_KEY",
608-
sessionToken: "STS_AR_SESSION_TOKEN",
643+
sessionToken: "STS_AR_SESSION_TOKEN_us-west-2",
609644
expiration: new Date("3000-01-01T00:00:00.000Z"),
610645
$source: {
611646
CREDENTIALS_CODE: "e",
@@ -650,7 +685,7 @@ describe("credential-provider-node integration test", () => {
650685
expect(credentials).toEqual({
651686
accessKeyId: "STS_AR_ACCESS_KEY_ID",
652687
secretAccessKey: "STS_AR_SECRET_ACCESS_KEY",
653-
sessionToken: "STS_AR_SESSION_TOKEN",
688+
sessionToken: "STS_AR_SESSION_TOKEN_us-west-2",
654689
expiration: new Date("3000-01-01T00:00:00.000Z"),
655690
$source: {
656691
CREDENTIALS_CODE: "e",
@@ -702,7 +737,7 @@ describe("credential-provider-node integration test", () => {
702737
expect(credentials).toEqual({
703738
accessKeyId: "STS_AR_ACCESS_KEY_ID",
704739
secretAccessKey: "STS_AR_SECRET_ACCESS_KEY",
705-
sessionToken: "STS_AR_SESSION_TOKEN",
740+
sessionToken: "STS_AR_SESSION_TOKEN_us-west-2",
706741
expiration: new Date("3000-01-01T00:00:00.000Z"),
707742
$source: {
708743
CREDENTIALS_CODE: "e",
@@ -753,7 +788,7 @@ describe("credential-provider-node integration test", () => {
753788
expect(credentials).toEqual({
754789
accessKeyId: "STS_ARWI_ACCESS_KEY_ID",
755790
secretAccessKey: "STS_ARWI_SECRET_ACCESS_KEY",
756-
sessionToken: "STS_ARWI_SESSION_TOKEN",
791+
sessionToken: "STS_ARWI_SESSION_TOKEN_us-west-2",
757792
expiration: new Date("3000-01-01T00:00:00.000Z"),
758793
$source: {
759794
CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN: "h",

packages/credential-provider-web-identity/src/fromWebToken.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
import { setCredentialFeature } from "@aws-sdk/core/client";
2-
import type { CredentialProviderOptions } from "@aws-sdk/types";
3-
import type { AwsCredentialIdentity, AwsCredentialIdentityProvider, Pluggable } from "@smithy/types";
1+
import type {
2+
AwsIdentityProperties,
3+
CredentialProviderOptions,
4+
RegionalAwsCredentialIdentityProvider,
5+
} from "@aws-sdk/types";
6+
import type { AwsCredentialIdentity, Pluggable } from "@smithy/types";
47

58
/**
69
* @public
@@ -152,8 +155,8 @@ export interface FromWebTokenInit
152155
* @internal
153156
*/
154157
export const fromWebToken =
155-
(init: FromWebTokenInit): AwsCredentialIdentityProvider =>
156-
async () => {
158+
(init: FromWebTokenInit): RegionalAwsCredentialIdentityProvider =>
159+
async (awsIdentityProperties?: AwsIdentityProperties) => {
157160
init.logger?.debug("@aws-sdk/credential-provider-web-identity - fromWebToken");
158161
const { roleArn, roleSessionName, webIdentityToken, providerId, policyArns, policy, durationSeconds } = init;
159162

@@ -166,7 +169,10 @@ export const fromWebToken =
166169
{
167170
...init.clientConfig,
168171
credentialProviderLogger: init.logger,
169-
parentClientConfig: init.parentClientConfig,
172+
parentClientConfig: {
173+
region: awsIdentityProperties?.contextClientConfig?.region,
174+
...init.parentClientConfig,
175+
},
170176
},
171177
init.clientPlugins
172178
);

packages/credential-providers/src/fromIni.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { fromIni as _fromIni, FromIniInit } from "@aws-sdk/credential-provider-ini";
2-
import { AwsCredentialIdentityProvider } from "@smithy/types";
2+
import type { RegionalAwsCredentialIdentityProvider } from "@aws-sdk/types";
33

44
/**
55
* Creates a credential provider function that reads from a shared credentials file at `~/.aws/credentials` and a
@@ -40,7 +40,7 @@ import { AwsCredentialIdentityProvider } from "@smithy/types";
4040
* });
4141
* ```
4242
*/
43-
export const fromIni = (init: FromIniInit = {}): AwsCredentialIdentityProvider =>
43+
export const fromIni = (init: FromIniInit = {}): RegionalAwsCredentialIdentityProvider =>
4444
_fromIni({
4545
...init,
4646
});

packages/middleware-signing/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,6 @@
22

33
[![NPM version](https://img.shields.io/npm/v/@aws-sdk/middleware-signing/latest.svg)](https://www.npmjs.com/package/@aws-sdk/middleware-signing)
44
[![NPM downloads](https://img.shields.io/npm/dm/@aws-sdk/middleware-signing.svg)](https://www.npmjs.com/package/@aws-sdk/middleware-signing)
5+
6+
This package is deprecated. It is only used in "legacy auth", and no longer used in the
7+
AWS SDK as of the Smithy Reference Architecture implementation of identity and auth.

0 commit comments

Comments
 (0)