From 089f1a42e58c7a85c0720c83ae5354e75a253695 Mon Sep 17 00:00:00 2001 From: George Fu Date: Tue, 8 Oct 2024 12:19:57 -0400 Subject: [PATCH] chore(credential-providers): add credential attribution (#6546) * chore(credential-providers): add credential attribution * chore(credential-providers): attribute credential feature sources * test(credential-provider-node): add credential source assertions * test: fix unit test * test(credential-providers): fix unit tests * chore(core): separate function files * chore: undo script change * chore: change Promise.resolve to async fn --- .../client-sts/src/defaultStsRoleAssumers.ts | 12 +- .../sts-client-defaultStsRoleAssumers.ts | 12 +- packages/core/src/submodules/client/index.ts | 1 + .../client/setCredentialFeature.spec.ts | 40 ++++++ .../submodules/client/setCredentialFeature.ts | 18 +++ .../aws_sdk/resolveAwsSdkSigV4Config.ts | 11 +- packages/credential-provider-env/package.json | 1 + .../src/fromEnv.spec.ts | 6 + .../credential-provider-env/src/fromEnv.ts | 9 +- .../credential-provider-http/package.json | 1 + .../src/fromHttp/fromHttp.ts | 3 +- packages/credential-provider-ini/package.json | 1 + .../src/resolveAssumeRoleCredentials.spec.ts | 6 +- .../src/resolveAssumeRoleCredentials.ts | 7 +- .../src/resolveCredentialSource.ts | 12 +- .../src/resolveProcessCredentials.ts | 3 +- .../src/resolveProfileData.spec.ts | 2 +- .../src/resolveProfileData.ts | 2 +- .../src/resolveSsoCredentials.spec.ts | 6 +- .../src/resolveSsoCredentials.ts | 17 ++- .../src/resolveStaticCredentials.spec.ts | 3 + .../src/resolveStaticCredentials.ts | 10 +- .../src/resolveWebIdentityCredentials.ts | 3 +- .../credential-provider-node.integ.spec.ts | 119 +++++++++++++++--- .../credential-provider-process/package.json | 1 + .../getValidatedProcessCredentials.spec.ts | 8 +- .../src/getValidatedProcessCredentials.ts | 7 +- packages/credential-provider-sso/package.json | 1 + .../src/resolveSSOCredentials.ts | 11 +- .../package.json | 1 + .../src/fromTokenFile.ts | 11 +- .../src/fromWebToken.ts | 1 + packages/credential-providers/package.json | 1 + .../src/fromInstanceMetadata.ts | 4 +- .../src/check-features.ts | 19 ++- .../src/middleware-user-agent.integ.spec.ts | 1 - packages/types/src/feature-ids.ts | 10 +- .../src/identity/AwsCredentialIdentity.ts | 8 ++ 38 files changed, 326 insertions(+), 63 deletions(-) create mode 100644 packages/core/src/submodules/client/setCredentialFeature.spec.ts create mode 100644 packages/core/src/submodules/client/setCredentialFeature.ts diff --git a/clients/client-sts/src/defaultStsRoleAssumers.ts b/clients/client-sts/src/defaultStsRoleAssumers.ts index 9daf5da2e9128..d29c654f66b87 100644 --- a/clients/client-sts/src/defaultStsRoleAssumers.ts +++ b/clients/client-sts/src/defaultStsRoleAssumers.ts @@ -1,6 +1,7 @@ // smithy-typescript generated code // Please do not touch this file. It's generated from template in: // 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 +import { setCredentialFeature } from "@aws-sdk/core/client"; import type { CredentialProviderOptions } from "@aws-sdk/types"; import { AwsCredentialIdentity, Logger, Provider } from "@smithy/types"; @@ -118,7 +119,7 @@ export const getDefaultRoleAssumer = ( const accountId = getAccountIdFromAssumedRoleUser(AssumedRoleUser); - return { + const credentials = { accessKeyId: Credentials.AccessKeyId, secretAccessKey: Credentials.SecretAccessKey, sessionToken: Credentials.SessionToken, @@ -127,6 +128,8 @@ export const getDefaultRoleAssumer = ( ...((Credentials as any).CredentialScope && { credentialScope: (Credentials as any).CredentialScope }), ...(accountId && { accountId }), }; + setCredentialFeature(credentials, "CREDENTIALS_STS_ASSUME_ROLE", "i"); + return credentials; }; }; @@ -174,7 +177,7 @@ export const getDefaultRoleAssumerWithWebIdentity = ( const accountId = getAccountIdFromAssumedRoleUser(AssumedRoleUser); - return { + const credentials = { accessKeyId: Credentials.AccessKeyId, secretAccessKey: Credentials.SecretAccessKey, sessionToken: Credentials.SessionToken, @@ -183,6 +186,11 @@ export const getDefaultRoleAssumerWithWebIdentity = ( ...((Credentials as any).CredentialScope && { credentialScope: (Credentials as any).CredentialScope }), ...(accountId && { accountId }), }; + if (accountId) { + setCredentialFeature(credentials, "RESOLVED_ACCOUNT_ID", "T"); + } + setCredentialFeature(credentials, "CREDENTIALS_STS_ASSUME_ROLE_WEB_ID", "k"); + return credentials; }; }; diff --git a/codegen/smithy-aws-typescript-codegen/src/main/resources/software/amazon/smithy/aws/typescript/codegen/sts-client-defaultStsRoleAssumers.ts b/codegen/smithy-aws-typescript-codegen/src/main/resources/software/amazon/smithy/aws/typescript/codegen/sts-client-defaultStsRoleAssumers.ts index f1183d03e993d..dd21da12895ef 100644 --- a/codegen/smithy-aws-typescript-codegen/src/main/resources/software/amazon/smithy/aws/typescript/codegen/sts-client-defaultStsRoleAssumers.ts +++ b/codegen/smithy-aws-typescript-codegen/src/main/resources/software/amazon/smithy/aws/typescript/codegen/sts-client-defaultStsRoleAssumers.ts @@ -1,3 +1,4 @@ +import { setCredentialFeature } from "@aws-sdk/core/client"; import type { CredentialProviderOptions } from "@aws-sdk/types"; import { AwsCredentialIdentity, Logger, Provider } from "@smithy/types"; @@ -115,7 +116,7 @@ export const getDefaultRoleAssumer = ( const accountId = getAccountIdFromAssumedRoleUser(AssumedRoleUser); - return { + const credentials = { accessKeyId: Credentials.AccessKeyId, secretAccessKey: Credentials.SecretAccessKey, sessionToken: Credentials.SessionToken, @@ -124,6 +125,8 @@ export const getDefaultRoleAssumer = ( ...((Credentials as any).CredentialScope && { credentialScope: (Credentials as any).CredentialScope }), ...(accountId && { accountId }), }; + setCredentialFeature(credentials, "CREDENTIALS_STS_ASSUME_ROLE", "i"); + return credentials; }; }; @@ -171,7 +174,7 @@ export const getDefaultRoleAssumerWithWebIdentity = ( const accountId = getAccountIdFromAssumedRoleUser(AssumedRoleUser); - return { + const credentials = { accessKeyId: Credentials.AccessKeyId, secretAccessKey: Credentials.SecretAccessKey, sessionToken: Credentials.SessionToken, @@ -180,6 +183,11 @@ export const getDefaultRoleAssumerWithWebIdentity = ( ...((Credentials as any).CredentialScope && { credentialScope: (Credentials as any).CredentialScope }), ...(accountId && { accountId }), }; + if (accountId) { + setCredentialFeature(credentials, "RESOLVED_ACCOUNT_ID", "T"); + } + setCredentialFeature(credentials, "CREDENTIALS_STS_ASSUME_ROLE_WEB_ID", "k"); + return credentials; }; }; diff --git a/packages/core/src/submodules/client/index.ts b/packages/core/src/submodules/client/index.ts index 2c463d5f978ff..1a2cc9d139ff2 100644 --- a/packages/core/src/submodules/client/index.ts +++ b/packages/core/src/submodules/client/index.ts @@ -1,2 +1,3 @@ export * from "./emitWarningIfUnsupportedVersion"; +export * from "./setCredentialFeature"; export * from "./setFeature"; diff --git a/packages/core/src/submodules/client/setCredentialFeature.spec.ts b/packages/core/src/submodules/client/setCredentialFeature.spec.ts new file mode 100644 index 0000000000000..02f52b998e8af --- /dev/null +++ b/packages/core/src/submodules/client/setCredentialFeature.spec.ts @@ -0,0 +1,40 @@ +import { AttributedAwsCredentialIdentity } from "@aws-sdk/types"; + +import { setCredentialFeature } from "./setCredentialFeature"; + +describe(setCredentialFeature.name, () => { + it("should create the data path if it does't exist", () => { + const credentials = { + accessKeyId: "", + secretAccessKey: "", + } as AttributedAwsCredentialIdentity; + expect(setCredentialFeature(credentials, "CREDENTIALS_CODE", "e")).toEqual({ + accessKeyId: "", + secretAccessKey: "", + $source: { + CREDENTIALS_CODE: "e", + }, + }); + }); + + it("should track a set of features", () => { + const credentials = { + accessKeyId: "", + secretAccessKey: "", + } as AttributedAwsCredentialIdentity; + + setCredentialFeature(credentials, "CREDENTIALS_CODE", "e"); + setCredentialFeature(credentials, "CREDENTIALS_ENV_VARS", "g"); + // it ignores duplicates. + setCredentialFeature(credentials, "CREDENTIALS_ENV_VARS", "g"); + + expect(setCredentialFeature(credentials, "CREDENTIALS_CODE", "e")).toEqual({ + accessKeyId: "", + secretAccessKey: "", + $source: { + CREDENTIALS_CODE: "e", + CREDENTIALS_ENV_VARS: "g", + }, + }); + }); +}); diff --git a/packages/core/src/submodules/client/setCredentialFeature.ts b/packages/core/src/submodules/client/setCredentialFeature.ts new file mode 100644 index 0000000000000..3249f31249b50 --- /dev/null +++ b/packages/core/src/submodules/client/setCredentialFeature.ts @@ -0,0 +1,18 @@ +import type { AttributedAwsCredentialIdentity, AwsSdkCredentialsFeatures } from "@aws-sdk/types"; + +/** + * @internal + * + * @returns the credentials with source feature attribution. + */ +export function setCredentialFeature( + credentials: AttributedAwsCredentialIdentity, + feature: F, + value: AwsSdkCredentialsFeatures[F] +): AttributedAwsCredentialIdentity { + if (!credentials.$source) { + credentials.$source = {}; + } + credentials.$source![feature] = value; + return credentials; +} diff --git a/packages/core/src/submodules/httpAuthSchemes/aws_sdk/resolveAwsSdkSigV4Config.ts b/packages/core/src/submodules/httpAuthSchemes/aws_sdk/resolveAwsSdkSigV4Config.ts index c70d7a9e37d52..5d19419bccb50 100644 --- a/packages/core/src/submodules/httpAuthSchemes/aws_sdk/resolveAwsSdkSigV4Config.ts +++ b/packages/core/src/submodules/httpAuthSchemes/aws_sdk/resolveAwsSdkSigV4Config.ts @@ -1,3 +1,5 @@ +import { setCredentialFeature } from "@aws-sdk/core/client"; +import { AttributedAwsCredentialIdentity } from "@aws-sdk/types"; import { doesIdentityRequireRefresh, isIdentityExpired, @@ -102,9 +104,11 @@ export interface AwsSdkSigV4AuthResolvedConfig { export const resolveAwsSdkSigV4Config = ( config: T & AwsSdkSigV4AuthInputConfig & AwsSdkSigV4PreviouslyResolved ): T & AwsSdkSigV4AuthResolvedConfig => { + let isUserSupplied = false; // Normalize credentials let normalizedCreds: AwsCredentialIdentityProvider | undefined; if (config.credentials) { + isUserSupplied = true; normalizedCreds = memoizeIdentityProvider(config.credentials, isIdentityExpired, doesIdentityRequireRefresh); } if (!normalizedCreds) { @@ -218,7 +222,12 @@ export const resolveAwsSdkSigV4Config = ( ...config, systemClockOffset, signingEscapePath, - credentials: normalizedCreds!, + credentials: isUserSupplied + ? async () => + normalizedCreds!().then((creds: AttributedAwsCredentialIdentity) => + setCredentialFeature(creds, "CREDENTIALS_CODE", "e") + ) + : normalizedCreds!, signer, }; }; diff --git a/packages/credential-provider-env/package.json b/packages/credential-provider-env/package.json index 7a895656da593..4354ea5a15524 100644 --- a/packages/credential-provider-env/package.json +++ b/packages/credential-provider-env/package.json @@ -24,6 +24,7 @@ }, "license": "Apache-2.0", "dependencies": { + "@aws-sdk/core": "*", "@aws-sdk/types": "*", "@smithy/property-provider": "^3.1.7", "@smithy/types": "^3.5.0", diff --git a/packages/credential-provider-env/src/fromEnv.spec.ts b/packages/credential-provider-env/src/fromEnv.spec.ts index 596b9234bb4e9..f75430bdca856 100644 --- a/packages/credential-provider-env/src/fromEnv.spec.ts +++ b/packages/credential-provider-env/src/fromEnv.spec.ts @@ -33,6 +33,9 @@ describe(fromEnv.name, () => { sessionToken: mockSessionToken, expiration: new Date(mockExpiration), accountId: mockAccountId, + $source: { + CREDENTIALS_ENV_VARS: "g", + }, }); }); @@ -44,6 +47,9 @@ describe(fromEnv.name, () => { expect(receivedCreds).toStrictEqual({ accessKeyId: mockAccessKeyId, secretAccessKey: mockSecretAccessKey, + $source: { + CREDENTIALS_ENV_VARS: "g", + }, }); }); diff --git a/packages/credential-provider-env/src/fromEnv.ts b/packages/credential-provider-env/src/fromEnv.ts index 71215e3f83f0b..1dfe7f96fb8b4 100644 --- a/packages/credential-provider-env/src/fromEnv.ts +++ b/packages/credential-provider-env/src/fromEnv.ts @@ -1,4 +1,5 @@ -import type { CredentialProviderOptions } from "@aws-sdk/types"; +import { setCredentialFeature } from "@aws-sdk/core/client"; +import type { AttributedAwsCredentialIdentity, CredentialProviderOptions } from "@aws-sdk/types"; import { CredentialsProviderError } from "@smithy/property-provider"; import { AwsCredentialIdentityProvider } from "@smithy/types"; @@ -48,14 +49,16 @@ export const fromEnv = const accountId: string | undefined = process.env[ENV_ACCOUNT_ID]; if (accessKeyId && secretAccessKey) { - return { + const credentials = { accessKeyId, secretAccessKey, ...(sessionToken && { sessionToken }), ...(expiry && { expiration: new Date(expiry) }), ...(credentialScope && { credentialScope }), ...(accountId && { accountId }), - }; + } as AttributedAwsCredentialIdentity; + setCredentialFeature(credentials, "CREDENTIALS_ENV_VARS", "g"); + return credentials; } throw new CredentialsProviderError("Unable to find environment variable credentials.", { logger: init?.logger }); diff --git a/packages/credential-provider-http/package.json b/packages/credential-provider-http/package.json index ebdb39c455b37..ecdcb144ae359 100644 --- a/packages/credential-provider-http/package.json +++ b/packages/credential-provider-http/package.json @@ -26,6 +26,7 @@ }, "license": "Apache-2.0", "dependencies": { + "@aws-sdk/core": "*", "@aws-sdk/types": "*", "@smithy/fetch-http-handler": "^3.2.9", "@smithy/node-http-handler": "^3.2.4", diff --git a/packages/credential-provider-http/src/fromHttp/fromHttp.ts b/packages/credential-provider-http/src/fromHttp/fromHttp.ts index 287ab3a12a931..f505bcc6323b7 100644 --- a/packages/credential-provider-http/src/fromHttp/fromHttp.ts +++ b/packages/credential-provider-http/src/fromHttp/fromHttp.ts @@ -1,3 +1,4 @@ +import { setCredentialFeature } from "@aws-sdk/core/client"; import { NodeHttpHandler } from "@smithy/node-http-handler"; import { CredentialsProviderError } from "@smithy/property-provider"; import { AwsCredentialIdentity, AwsCredentialIdentityProvider } from "@smithy/types"; @@ -81,7 +82,7 @@ Set AWS_CONTAINER_CREDENTIALS_FULL_URI or AWS_CONTAINER_CREDENTIALS_RELATIVE_URI } try { const result = await requestHandler.handle(request); - return getCredentials(result.response); + return getCredentials(result.response).then((creds) => setCredentialFeature(creds, "CREDENTIALS_HTTP", "z")); } catch (e: unknown) { throw new CredentialsProviderError(String(e), { logger: options.logger }); } diff --git a/packages/credential-provider-ini/package.json b/packages/credential-provider-ini/package.json index f2ede3745bc65..ee7d0f3437351 100644 --- a/packages/credential-provider-ini/package.json +++ b/packages/credential-provider-ini/package.json @@ -24,6 +24,7 @@ }, "license": "Apache-2.0", "dependencies": { + "@aws-sdk/core": "*", "@aws-sdk/credential-provider-env": "*", "@aws-sdk/credential-provider-http": "*", "@aws-sdk/credential-provider-process": "*", diff --git a/packages/credential-provider-ini/src/resolveAssumeRoleCredentials.spec.ts b/packages/credential-provider-ini/src/resolveAssumeRoleCredentials.spec.ts index a0e8c8ac0222a..b36372fd6dd52 100644 --- a/packages/credential-provider-ini/src/resolveAssumeRoleCredentials.spec.ts +++ b/packages/credential-provider-ini/src/resolveAssumeRoleCredentials.spec.ts @@ -7,7 +7,7 @@ import { resolveProfileData } from "./resolveProfileData"; jest.mock("@aws-sdk/client-sts", () => { return { - getDefaultRoleAssumer: jest.fn().mockReturnValue(() => {}), + getDefaultRoleAssumer: jest.fn().mockReturnValue(async () => ({})), }; }); jest.mock("@smithy/shared-ini-file-loader"); @@ -98,7 +98,7 @@ describe(resolveAssumeRoleCredentials.name, () => { const mockProfiles = { [mockProfileName]: {} }; const mockOptions = { mfaCodeProvider: jest.fn(), - roleAssumer: jest.fn().mockReturnValue(mockCreds), + roleAssumer: jest.fn().mockReturnValue(Promise.resolve(mockCreds)), roleAssumerWithWebIdentity: jest.fn(), }; const mockCredentialSource = "mockCredentialSource"; @@ -120,7 +120,7 @@ describe(resolveAssumeRoleCredentials.name, () => { beforeEach(() => { (getProfileName as jest.Mock).mockReturnValue(mockProfileName); (resolveProfileData as jest.Mock).mockResolvedValue(mockSourceCredsFromProfile); - (resolveCredentialSource as jest.Mock).mockReturnValue(() => () => Promise.resolve(mockSourceCredsFromCredential)); + (resolveCredentialSource as jest.Mock).mockReturnValue(async () => async () => mockSourceCredsFromCredential); }); afterEach(() => { diff --git a/packages/credential-provider-ini/src/resolveAssumeRoleCredentials.ts b/packages/credential-provider-ini/src/resolveAssumeRoleCredentials.ts index 8cf9ac29c5ae0..a7bed9952747f 100644 --- a/packages/credential-provider-ini/src/resolveAssumeRoleCredentials.ts +++ b/packages/credential-provider-ini/src/resolveAssumeRoleCredentials.ts @@ -1,3 +1,4 @@ +import { setCredentialFeature } from "@aws-sdk/core/client"; import { CredentialsProviderError } from "@smithy/property-provider"; import { getProfileName } from "@smithy/shared-ini-file-loader"; import { AwsCredentialIdentity, IniSection, Logger, ParsedIniData, Profile } from "@smithy/types"; @@ -159,7 +160,7 @@ export const resolveAssumeRoleCredentials = async ( * can use its role_arn instead of redundantly needing another role_arn at * this final layer. */ - return sourceCredsProvider; + return sourceCredsProvider.then((creds) => setCredentialFeature(creds, "CREDENTIALS_PROFILE_SOURCE_PROFILE", "o")); } else { const params: AssumeRoleParams = { RoleArn: data.role_arn!, @@ -181,7 +182,9 @@ export const resolveAssumeRoleCredentials = async ( } const sourceCreds = await sourceCredsProvider; - return options.roleAssumer!(sourceCreds, params); + return options.roleAssumer!(sourceCreds, params).then((creds) => + setCredentialFeature(creds, "CREDENTIALS_PROFILE_SOURCE_PROFILE", "o") + ); } }; diff --git a/packages/credential-provider-ini/src/resolveCredentialSource.ts b/packages/credential-provider-ini/src/resolveCredentialSource.ts index 8c4efb47617f0..c85fc7b2fe3cc 100644 --- a/packages/credential-provider-ini/src/resolveCredentialSource.ts +++ b/packages/credential-provider-ini/src/resolveCredentialSource.ts @@ -1,4 +1,5 @@ -import type { CredentialProviderOptions } from "@aws-sdk/types"; +import { setCredentialFeature } from "@aws-sdk/core/client"; +import type { AwsCredentialIdentity, CredentialProviderOptions } from "@aws-sdk/types"; import { chain, CredentialsProviderError } from "@smithy/property-provider"; import { AwsCredentialIdentityProvider, Logger } from "@smithy/types"; @@ -21,17 +22,17 @@ export const resolveCredentialSource = ( const { fromHttp } = await import("@aws-sdk/credential-provider-http"); const { fromContainerMetadata } = await import("@smithy/credential-provider-imds"); logger?.debug("@aws-sdk/credential-provider-ini - credential_source is EcsContainer"); - return chain(fromHttp(options ?? {}), fromContainerMetadata(options)); + return async () => chain(fromHttp(options ?? {}), fromContainerMetadata(options))().then(setNamedProvider); }, Ec2InstanceMetadata: async (options?: CredentialProviderOptions) => { logger?.debug("@aws-sdk/credential-provider-ini - credential_source is Ec2InstanceMetadata"); const { fromInstanceMetadata } = await import("@smithy/credential-provider-imds"); - return fromInstanceMetadata(options); + return async () => fromInstanceMetadata(options)().then(setNamedProvider); }, Environment: async (options?: CredentialProviderOptions) => { logger?.debug("@aws-sdk/credential-provider-ini - credential_source is Environment"); const { fromEnv } = await import("@aws-sdk/credential-provider-env"); - return fromEnv(options); + return async () => fromEnv(options)().then(setNamedProvider); }, }; if (credentialSource in sourceProvidersMap) { @@ -44,3 +45,6 @@ export const resolveCredentialSource = ( ); } }; + +const setNamedProvider = (creds: AwsCredentialIdentity) => + setCredentialFeature(creds, "CREDENTIALS_PROFILE_NAMED_PROVIDER", "p"); diff --git a/packages/credential-provider-ini/src/resolveProcessCredentials.ts b/packages/credential-provider-ini/src/resolveProcessCredentials.ts index 152f9db5d11a1..bcff08d22412c 100644 --- a/packages/credential-provider-ini/src/resolveProcessCredentials.ts +++ b/packages/credential-provider-ini/src/resolveProcessCredentials.ts @@ -1,3 +1,4 @@ +import { setCredentialFeature } from "@aws-sdk/core/client"; import { Credentials, Profile } from "@aws-sdk/types"; import { FromIniInit } from "./fromIni"; @@ -23,5 +24,5 @@ export const resolveProcessCredentials = async (options: FromIniInit, profile: s fromProcess({ ...options, profile, - })() + })().then((creds) => setCredentialFeature(creds, "CREDENTIALS_PROFILE_PROCESS", "v")) ); diff --git a/packages/credential-provider-ini/src/resolveProfileData.spec.ts b/packages/credential-provider-ini/src/resolveProfileData.spec.ts index 6867998e30491..61576b34afab2 100644 --- a/packages/credential-provider-ini/src/resolveProfileData.spec.ts +++ b/packages/credential-provider-ini/src/resolveProfileData.spec.ts @@ -117,6 +117,6 @@ describe(resolveProfileData.name, () => { (resolveSsoCredentials as jest.Mock).mockImplementation(() => Promise.resolve(mockCreds)); const receivedCreds = await resolveProfileData(mockProfileName, mockProfiles, mockOptions); expect(receivedCreds).toStrictEqual(mockCreds); - expect(resolveSsoCredentials).toHaveBeenCalledWith(mockProfileName, mockOptions); + expect(resolveSsoCredentials).toHaveBeenCalledWith(mockProfileName, {}, mockOptions); }); }); diff --git a/packages/credential-provider-ini/src/resolveProfileData.ts b/packages/credential-provider-ini/src/resolveProfileData.ts index 42a4b30115dc4..522318b07d8f0 100644 --- a/packages/credential-provider-ini/src/resolveProfileData.ts +++ b/packages/credential-provider-ini/src/resolveProfileData.ts @@ -59,7 +59,7 @@ export const resolveProfileData = async ( } if (isSsoProfile(data)) { - return await resolveSsoCredentials(profileName, options); + return await resolveSsoCredentials(profileName, data, options); } // If the profile cannot be parsed or contains neither static credentials diff --git a/packages/credential-provider-ini/src/resolveSsoCredentials.spec.ts b/packages/credential-provider-ini/src/resolveSsoCredentials.spec.ts index ee943ae9170d4..51f2770c672f9 100644 --- a/packages/credential-provider-ini/src/resolveSsoCredentials.spec.ts +++ b/packages/credential-provider-ini/src/resolveSsoCredentials.spec.ts @@ -1,4 +1,4 @@ -import { fromSSO, validateSsoProfile } from "@aws-sdk/credential-provider-sso"; +import { fromSSO } from "@aws-sdk/credential-provider-sso"; import { AwsCredentialIdentity } from "@smithy/types"; import { isSsoProfile, resolveSsoCredentials } from "./resolveSsoCredentials"; @@ -30,7 +30,7 @@ describe(resolveSsoCredentials.name, () => { (fromSSO as jest.Mock).mockReturnValue(() => Promise.reject(expectedError)); try { - await resolveSsoCredentials(mockProfileName); + await resolveSsoCredentials(mockProfileName, {}); fail(`expected ${expectedError}`); } catch (error) { expect(error).toStrictEqual(expectedError); @@ -49,7 +49,7 @@ describe(resolveSsoCredentials.name, () => { (fromSSO as jest.Mock).mockReturnValue(() => Promise.resolve(mockCreds)); - const receivedCreds = await resolveSsoCredentials(mockProfileName); + const receivedCreds = await resolveSsoCredentials(mockProfileName, {}); expect(receivedCreds).toStrictEqual(mockCreds); expect(fromSSO).toHaveBeenCalledWith({ profile: mockProfileName, diff --git a/packages/credential-provider-ini/src/resolveSsoCredentials.ts b/packages/credential-provider-ini/src/resolveSsoCredentials.ts index ca0f0271559b1..e26ac7b5726d1 100644 --- a/packages/credential-provider-ini/src/resolveSsoCredentials.ts +++ b/packages/credential-provider-ini/src/resolveSsoCredentials.ts @@ -1,16 +1,27 @@ +import { setCredentialFeature } from "@aws-sdk/core/client"; import type { SsoProfile } from "@aws-sdk/credential-provider-sso"; import type { CredentialProviderOptions } from "@aws-sdk/types"; -import type { Profile } from "@smithy/types"; +import type { IniSection, Profile } from "@smithy/types"; /** * @internal */ -export const resolveSsoCredentials = async (profile: string, options: CredentialProviderOptions = {}) => { +export const resolveSsoCredentials = async ( + profile: string, + profileData: IniSection, + options: CredentialProviderOptions = {} +) => { const { fromSSO } = await import("@aws-sdk/credential-provider-sso"); return fromSSO({ profile, logger: options.logger, - })(); + })().then((creds) => { + if (profileData.sso_session) { + return setCredentialFeature(creds, "CREDENTIALS_PROFILE_SSO", "r"); + } else { + return setCredentialFeature(creds, "CREDENTIALS_PROFILE_SSO_LEGACY", "t"); + } + }); }; /** diff --git a/packages/credential-provider-ini/src/resolveStaticCredentials.spec.ts b/packages/credential-provider-ini/src/resolveStaticCredentials.spec.ts index 00da15e3144f1..c927e07f30ab9 100644 --- a/packages/credential-provider-ini/src/resolveStaticCredentials.spec.ts +++ b/packages/credential-provider-ini/src/resolveStaticCredentials.spec.ts @@ -54,6 +54,9 @@ describe(resolveStaticCredentials.name, () => { sessionToken: mockProfile.aws_session_token, credentialScope: mockProfile.aws_credential_scope, accountId: mockProfile.aws_account_id, + $source: { + CREDENTIALS_PROFILE: "n", + }, }); }); }); diff --git a/packages/credential-provider-ini/src/resolveStaticCredentials.ts b/packages/credential-provider-ini/src/resolveStaticCredentials.ts index a778252f1d95d..b82a299c1801a 100644 --- a/packages/credential-provider-ini/src/resolveStaticCredentials.ts +++ b/packages/credential-provider-ini/src/resolveStaticCredentials.ts @@ -1,3 +1,4 @@ +import { setCredentialFeature } from "@aws-sdk/core/client"; import { AwsCredentialIdentity, Profile } from "@smithy/types"; import { FromIniInit } from "./fromIni"; @@ -27,16 +28,19 @@ export const isStaticCredsProfile = (arg: any): arg is StaticCredsProfile => /** * @internal */ -export const resolveStaticCredentials = ( +export const resolveStaticCredentials = async ( profile: StaticCredsProfile, options?: FromIniInit ): Promise => { options?.logger?.debug("@aws-sdk/credential-provider-ini - resolveStaticCredentials"); - return Promise.resolve({ + + const credentials = { accessKeyId: profile.aws_access_key_id, secretAccessKey: profile.aws_secret_access_key, sessionToken: profile.aws_session_token, ...(profile.aws_credential_scope && { credentialScope: profile.aws_credential_scope }), ...(profile.aws_account_id && { accountId: profile.aws_account_id }), - }); + }; + + return setCredentialFeature(credentials, "CREDENTIALS_PROFILE", "n"); }; diff --git a/packages/credential-provider-ini/src/resolveWebIdentityCredentials.ts b/packages/credential-provider-ini/src/resolveWebIdentityCredentials.ts index 8eef85d465c4f..04f33c7808205 100644 --- a/packages/credential-provider-ini/src/resolveWebIdentityCredentials.ts +++ b/packages/credential-provider-ini/src/resolveWebIdentityCredentials.ts @@ -1,3 +1,4 @@ +import { setCredentialFeature } from "@aws-sdk/core/client"; import { AwsCredentialIdentity, Profile } from "@smithy/types"; import { FromIniInit } from "./fromIni"; @@ -36,5 +37,5 @@ export const resolveWebIdentityCredentials = async ( roleAssumerWithWebIdentity: options.roleAssumerWithWebIdentity, logger: options.logger, parentClientConfig: options.parentClientConfig, - })() + })().then((creds) => setCredentialFeature(creds, "CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN", "q")) ); diff --git a/packages/credential-provider-node/src/credential-provider-node.integ.spec.ts b/packages/credential-provider-node/src/credential-provider-node.integ.spec.ts index 12d9e19867018..2625ad88531a3 100644 --- a/packages/credential-provider-node/src/credential-provider-node.integ.spec.ts +++ b/packages/credential-provider-node/src/credential-provider-node.integ.spec.ts @@ -295,6 +295,9 @@ describe("credential-provider-node integration test", () => { expect(credentials).toEqual({ accessKeyId: "ENV_ACCESS_KEY", secretAccessKey: "ENV_SECRET_KEY", + $source: { + CREDENTIALS_ENV_VARS: "g", + }, }); }); @@ -312,6 +315,9 @@ describe("credential-provider-node integration test", () => { expiration: new Date("2000-01-01T00:00:00.000Z"), sessionToken: "ENV_SESSION_TOKEN", credentialScope: "us-env-1", + $source: { + CREDENTIALS_ENV_VARS: "g", + }, }); }); @@ -331,6 +337,9 @@ describe("credential-provider-node integration test", () => { expect(credentials).toEqual({ accessKeyId: "INI_STATIC_ACCESS_KEY", secretAccessKey: "INI_STATIC_SECRET_KEY", + $source: { + CREDENTIALS_PROFILE: "n", + }, }); }); }); @@ -356,6 +365,10 @@ describe("credential-provider-node integration test", () => { sessionToken: "SSO_SESSION_TOKEN", expiration: new Date("3000-01-01T00:00:00.000Z"), credentialScope: "us-sso-1-us-sso-region-1", + $source: { + CREDENTIALS_CODE: "e", + CREDENTIALS_SSO_LEGACY: "u", + }, }); }); }); @@ -371,6 +384,9 @@ describe("credential-provider-node integration test", () => { expect(credentials).toEqual({ accessKeyId: "INI_STATIC_ACCESS_KEY", secretAccessKey: "INI_STATIC_SECRET_KEY", + $source: { + CREDENTIALS_PROFILE: "n", + }, }); }); @@ -395,6 +411,10 @@ describe("credential-provider-node integration test", () => { sessionToken: "STS_AR_SESSION_TOKEN", expiration: new Date("3000-01-01T00:00:00.000Z"), credentialScope: "us-stsar-1__us-west-2", + $source: { + CREDENTIALS_PROFILE_SOURCE_PROFILE: "o", + CREDENTIALS_STS_ASSUME_ROLE: "i", + }, }); }); @@ -423,6 +443,10 @@ describe("credential-provider-node integration test", () => { sessionToken: "STS_AR_SESSION_TOKEN", expiration: new Date("3000-01-01T00:00:00.000Z"), credentialScope: "us-stsar-1__eu-west-1", + $source: { + CREDENTIALS_PROFILE_SOURCE_PROFILE: "o", + CREDENTIALS_STS_ASSUME_ROLE: "i", + }, }); }); @@ -451,6 +475,10 @@ describe("credential-provider-node integration test", () => { sessionToken: "STS_AR_SESSION_TOKEN", expiration: new Date("3000-01-01T00:00:00.000Z"), credentialScope: "us-stsar-1__us-gov-stsar-1", + $source: { + CREDENTIALS_PROFILE_SOURCE_PROFILE: "o", + CREDENTIALS_STS_ASSUME_ROLE: "i", + }, }); }); @@ -471,29 +499,41 @@ describe("credential-provider-node integration test", () => { sessionToken: "STS_ARWI_SESSION_TOKEN", expiration: new Date("3000-01-01T00:00:00.000Z"), credentialScope: "us-stsarwi-1__us-west-2", + $source: { + CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN: "q", + CREDENTIALS_STS_ASSUME_ROLE_WEB_ID: "k", + }, }); }); - it("should resolve credentials from STS assumeRoleWithWebIdentity if the ini profile is configured for web identity and the client region is not the default AWS partition", async () => { - sts = new STS({ - region: "us-gov-sts-1", - requestHandler: mockRequestHandler, - }); - Object.assign(iniProfileData.default, { - region: "us-gov-sts-1", - web_identity_token_file: "token-filepath", - role_arn: "ROLE_ARN", - }); - await sts.getCallerIdentity({}); - const credentials = await sts.config.credentials(); - expect(credentials).toEqual({ - accessKeyId: "STS_ARWI_ACCESS_KEY_ID", - secretAccessKey: "STS_ARWI_SECRET_ACCESS_KEY", - sessionToken: "STS_ARWI_SESSION_TOKEN", - expiration: new Date("3000-01-01T00:00:00.000Z"), - credentialScope: "us-stsarwi-1__us-gov-sts-1", - }); - }); + it( + "should resolve credentials from STS assumeRoleWithWebIdentity if the ini profile is" + + " configured for web identity and the client region is not the default AWS partition", + async () => { + sts = new STS({ + region: "us-gov-sts-1", + requestHandler: mockRequestHandler, + }); + Object.assign(iniProfileData.default, { + region: "us-gov-sts-1", + web_identity_token_file: "token-filepath", + role_arn: "ROLE_ARN", + }); + await sts.getCallerIdentity({}); + const credentials = await sts.config.credentials(); + expect(credentials).toEqual({ + accessKeyId: "STS_ARWI_ACCESS_KEY_ID", + secretAccessKey: "STS_ARWI_SECRET_ACCESS_KEY", + sessionToken: "STS_ARWI_SESSION_TOKEN", + expiration: new Date("3000-01-01T00:00:00.000Z"), + credentialScope: "us-stsarwi-1__us-gov-sts-1", + $source: { + CREDENTIALS_PROFILE_STS_WEB_ID_TOKEN: "q", + CREDENTIALS_STS_ASSUME_ROLE_WEB_ID: "k", + }, + }); + } + ); it("should resolve process credentials if the profile is a process profile", async () => { Object.assign(iniProfileData.default, { @@ -506,6 +546,10 @@ describe("credential-provider-node integration test", () => { secretAccessKey: "PROCESS_SECRET_ACCESS_KEY", sessionToken: "PROCESS_SESSION_TOKEN", credentialScope: "us-process-1", + $source: { + CREDENTIALS_PROCESS: "w", + CREDENTIALS_PROFILE_PROCESS: "v", + }, }); }); @@ -529,6 +573,10 @@ describe("credential-provider-node integration test", () => { sessionToken: "SSO_SESSION_TOKEN", expiration: new Date("3000-01-01T00:00:00.000Z"), credentialScope: "us-sso-1-us-sso-region-1", + $source: { + CREDENTIALS_PROFILE_SSO: "r", + CREDENTIALS_SSO: "s", + }, }); }); @@ -561,6 +609,11 @@ describe("credential-provider-node integration test", () => { sessionToken: "STS_AR_SESSION_TOKEN", expiration: new Date("3000-01-01T00:00:00.000Z"), credentialScope: "us-stsar-1__us-west-2", + $source: { + CREDENTIALS_CODE: "e", + CREDENTIALS_PROFILE_SOURCE_PROFILE: "o", + CREDENTIALS_STS_ASSUME_ROLE: "i", + }, }); expect(spy).toHaveBeenCalledWith( expect.objectContaining({ @@ -601,6 +654,11 @@ describe("credential-provider-node integration test", () => { sessionToken: "STS_AR_SESSION_TOKEN", expiration: new Date("3000-01-01T00:00:00.000Z"), credentialScope: "us-stsar-1__us-west-2", + $source: { + CREDENTIALS_CODE: "e", + CREDENTIALS_PROFILE_SOURCE_PROFILE: "o", + CREDENTIALS_STS_ASSUME_ROLE: "i", + }, }); expect(assumeRoleArns).toEqual(["ROLE_ARN_1", "ROLE_ARN_2"]); }); @@ -643,6 +701,11 @@ describe("credential-provider-node integration test", () => { sessionToken: "STS_AR_SESSION_TOKEN", expiration: new Date("3000-01-01T00:00:00.000Z"), credentialScope: "us-stsar-1__us-west-2", + $source: { + CREDENTIALS_CODE: "e", + CREDENTIALS_PROFILE_SOURCE_PROFILE: "o", + CREDENTIALS_STS_ASSUME_ROLE: "i", + }, }); expect(spy).toHaveBeenCalledWith( expect.objectContaining({ @@ -692,6 +755,11 @@ describe("credential-provider-node integration test", () => { sessionToken: "STS_AR_SESSION_TOKEN", expiration: new Date("3000-01-01T00:00:00.000Z"), credentialScope: "us-stsar-1__us-west-2", + $source: { + CREDENTIALS_CODE: "e", + CREDENTIALS_PROFILE_SOURCE_PROFILE: "o", + CREDENTIALS_STS_ASSUME_ROLE: "i", + }, }); expect(spy).toHaveBeenCalledWith( expect.objectContaining({ @@ -720,6 +788,10 @@ describe("credential-provider-node integration test", () => { secretAccessKey: "PROCESS_SECRET_ACCESS_KEY", sessionToken: "PROCESS_SESSION_TOKEN", credentialScope: "us-process-1", + $source: { + CREDENTIALS_PROCESS: "w", + CREDENTIALS_PROFILE_PROCESS: "v", + }, }); }); }); @@ -736,6 +808,10 @@ describe("credential-provider-node integration test", () => { sessionToken: "STS_ARWI_SESSION_TOKEN", expiration: new Date("3000-01-01T00:00:00.000Z"), credentialScope: "us-stsarwi-1__us-west-2", + $source: { + CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN: "h", + CREDENTIALS_STS_ASSUME_ROLE_WEB_ID: "k", + }, }); }); }); @@ -751,6 +827,9 @@ describe("credential-provider-node integration test", () => { secretAccessKey: "CONTAINER_SECRET_ACCESS_KEY", sessionToken: "CONTAINER_TOKEN", expiration: new Date("3000-01-01T00:00:00.000Z"), + $source: { + CREDENTIALS_HTTP: "z", + }, }); }); diff --git a/packages/credential-provider-process/package.json b/packages/credential-provider-process/package.json index 000297499a6ec..b73d4720cd89b 100644 --- a/packages/credential-provider-process/package.json +++ b/packages/credential-provider-process/package.json @@ -24,6 +24,7 @@ }, "license": "Apache-2.0", "dependencies": { + "@aws-sdk/core": "*", "@aws-sdk/types": "*", "@smithy/property-provider": "^3.1.7", "@smithy/shared-ini-file-loader": "^3.1.8", diff --git a/packages/credential-provider-process/src/getValidatedProcessCredentials.spec.ts b/packages/credential-provider-process/src/getValidatedProcessCredentials.spec.ts index cc82de4d9c2d4..73dbfe23990b9 100644 --- a/packages/credential-provider-process/src/getValidatedProcessCredentials.spec.ts +++ b/packages/credential-provider-process/src/getValidatedProcessCredentials.spec.ts @@ -1,4 +1,5 @@ -import { AwsCredentialIdentity, ParsedIniData } from "@smithy/types"; +import { AttributedAwsCredentialIdentity } from "@aws-sdk/types"; +import { ParsedIniData } from "@smithy/types"; import { getValidatedProcessCredentials } from "./getValidatedProcessCredentials"; import { ProcessCredentials } from "./ProcessCredentials"; @@ -67,12 +68,15 @@ describe(getValidatedProcessCredentials.name, () => { }); describe("returns validated Process credentials", () => { - const getValidatedCredentials = (data: ProcessCredentials): AwsCredentialIdentity => ({ + const getValidatedCredentials = (data: ProcessCredentials): AttributedAwsCredentialIdentity => ({ accessKeyId: data.AccessKeyId, secretAccessKey: data.SecretAccessKey, ...(data.SessionToken && { sessionToken: data.SessionToken }), ...(data.Expiration && { expiration: new Date(data.Expiration) }), ...(data.AccountId && { accountId: data.AccountId }), + $source: { + CREDENTIALS_PROCESS: "w", + }, }); it("with all values", () => { diff --git a/packages/credential-provider-process/src/getValidatedProcessCredentials.ts b/packages/credential-provider-process/src/getValidatedProcessCredentials.ts index 6b0284972eca9..d3b1ae0149031 100644 --- a/packages/credential-provider-process/src/getValidatedProcessCredentials.ts +++ b/packages/credential-provider-process/src/getValidatedProcessCredentials.ts @@ -1,3 +1,4 @@ +import { setCredentialFeature } from "@aws-sdk/core/client"; import { AwsCredentialIdentity, ParsedIniData } from "@smithy/types"; import { ProcessCredentials } from "./ProcessCredentials"; @@ -31,7 +32,7 @@ export const getValidatedProcessCredentials = ( accountId = profiles[profileName].aws_account_id; } - return { + const credentials = { accessKeyId: data.AccessKeyId, secretAccessKey: data.SecretAccessKey, ...(data.SessionToken && { sessionToken: data.SessionToken }), @@ -39,4 +40,8 @@ export const getValidatedProcessCredentials = ( ...(data.CredentialScope && { credentialScope: data.CredentialScope }), ...(accountId && { accountId }), }; + + setCredentialFeature(credentials, "CREDENTIALS_PROCESS", "w"); + + return credentials; }; diff --git a/packages/credential-provider-sso/package.json b/packages/credential-provider-sso/package.json index bbc3715971f71..938df0060b9ff 100644 --- a/packages/credential-provider-sso/package.json +++ b/packages/credential-provider-sso/package.json @@ -24,6 +24,7 @@ }, "license": "Apache-2.0", "dependencies": { + "@aws-sdk/core": "*", "@aws-sdk/client-sso": "*", "@aws-sdk/token-providers": "*", "@aws-sdk/types": "*", diff --git a/packages/credential-provider-sso/src/resolveSSOCredentials.ts b/packages/credential-provider-sso/src/resolveSSOCredentials.ts index 48b598bd092e7..cb80be5ab0cba 100644 --- a/packages/credential-provider-sso/src/resolveSSOCredentials.ts +++ b/packages/credential-provider-sso/src/resolveSSOCredentials.ts @@ -1,3 +1,4 @@ +import { setCredentialFeature } from "@aws-sdk/core/client"; import { fromSso as getSsoTokenProvider } from "@aws-sdk/token-providers"; import { CredentialsProviderError } from "@smithy/property-provider"; import { getSSOTokenFromFile, SSOToken } from "@smithy/shared-ini-file-loader"; @@ -103,7 +104,7 @@ export const resolveSSOCredentials = async ({ }); } - return { + const credentials = { accessKeyId, secretAccessKey, sessionToken, @@ -111,4 +112,12 @@ export const resolveSSOCredentials = async ({ ...(credentialScope && { credentialScope }), ...(accountId && { accountId }), }; + + if (ssoSession) { + setCredentialFeature(credentials, "CREDENTIALS_SSO", "s"); + } else { + setCredentialFeature(credentials, "CREDENTIALS_SSO_LEGACY", "u"); + } + + return credentials; }; diff --git a/packages/credential-provider-web-identity/package.json b/packages/credential-provider-web-identity/package.json index 2e16d10a0f65e..4f1d855466caa 100644 --- a/packages/credential-provider-web-identity/package.json +++ b/packages/credential-provider-web-identity/package.json @@ -32,6 +32,7 @@ }, "license": "Apache-2.0", "dependencies": { + "@aws-sdk/core": "*", "@aws-sdk/types": "*", "@smithy/property-provider": "^3.1.7", "@smithy/types": "^3.5.0", diff --git a/packages/credential-provider-web-identity/src/fromTokenFile.ts b/packages/credential-provider-web-identity/src/fromTokenFile.ts index fc39fc626e305..fa93541bcca0e 100644 --- a/packages/credential-provider-web-identity/src/fromTokenFile.ts +++ b/packages/credential-provider-web-identity/src/fromTokenFile.ts @@ -1,4 +1,5 @@ -import { CredentialProviderOptions } from "@aws-sdk/types"; +import { setCredentialFeature } from "@aws-sdk/core/client"; +import { AttributedAwsCredentialIdentity, CredentialProviderOptions } from "@aws-sdk/types"; import { CredentialsProviderError } from "@smithy/property-provider"; import type { AwsCredentialIdentityProvider } from "@smithy/types"; import { readFileSync } from "fs"; @@ -40,10 +41,16 @@ export const fromTokenFile = }); } - return fromWebToken({ + const credentials: AttributedAwsCredentialIdentity = await fromWebToken({ ...init, webIdentityToken: readFileSync(webIdentityTokenFile, { encoding: "ascii" }), roleArn, roleSessionName, })(); + + if (webIdentityTokenFile === process.env[ENV_TOKEN_FILE]) { + setCredentialFeature(credentials, "CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN", "h"); + } + + return credentials; }; diff --git a/packages/credential-provider-web-identity/src/fromWebToken.ts b/packages/credential-provider-web-identity/src/fromWebToken.ts index cc550ea8f977e..166af9a70ce01 100644 --- a/packages/credential-provider-web-identity/src/fromWebToken.ts +++ b/packages/credential-provider-web-identity/src/fromWebToken.ts @@ -1,3 +1,4 @@ +import { setCredentialFeature } from "@aws-sdk/core/client"; import type { CredentialProviderOptions } from "@aws-sdk/types"; import type { AwsCredentialIdentity, AwsCredentialIdentityProvider, Pluggable } from "@smithy/types"; diff --git a/packages/credential-providers/package.json b/packages/credential-providers/package.json index eed0512010cec..492c77f4e0525 100644 --- a/packages/credential-providers/package.json +++ b/packages/credential-providers/package.json @@ -29,6 +29,7 @@ }, "license": "Apache-2.0", "dependencies": { + "@aws-sdk/core": "*", "@aws-sdk/client-cognito-identity": "*", "@aws-sdk/client-sso": "*", "@aws-sdk/client-sts": "*", diff --git a/packages/credential-providers/src/fromInstanceMetadata.ts b/packages/credential-providers/src/fromInstanceMetadata.ts index 00347eac5fe1c..50f9dc7e07908 100644 --- a/packages/credential-providers/src/fromInstanceMetadata.ts +++ b/packages/credential-providers/src/fromInstanceMetadata.ts @@ -1,3 +1,4 @@ +import { setCredentialFeature } from "@aws-sdk/core/client"; import type { CredentialProviderOptions } from "@aws-sdk/types"; import { fromInstanceMetadata as _fromInstanceMetadata, @@ -28,5 +29,6 @@ export const fromInstanceMetadata = ( init?: _RemoteProviderInit & CredentialProviderOptions ): AwsCredentialIdentityProvider => { init?.logger?.debug("@smithy/credential-provider-imds", "fromInstanceMetadata"); - return _fromInstanceMetadata(init); + return async () => + _fromInstanceMetadata(init)().then((creds) => setCredentialFeature(creds, "CREDENTIALS_IMDS", "0")); }; diff --git a/packages/middleware-user-agent/src/check-features.ts b/packages/middleware-user-agent/src/check-features.ts index c47446aba3cdb..4a747f89e70cb 100644 --- a/packages/middleware-user-agent/src/check-features.ts +++ b/packages/middleware-user-agent/src/check-features.ts @@ -1,13 +1,18 @@ import { setFeature } from "@aws-sdk/core"; import type { AccountIdEndpointMode } from "@aws-sdk/core/account-id-endpoint"; -import type { AwsHandlerExecutionContext } from "@aws-sdk/types"; +import type { + AttributedAwsCredentialIdentity, + AwsHandlerExecutionContext, + AwsSdkCredentialsFeatures, +} from "@aws-sdk/types"; import type { IHttpRequest } from "@smithy/protocol-http"; -import type { BuildHandlerArguments, Provider } from "@smithy/types"; +import type { AwsCredentialIdentityProvider, BuildHandlerArguments, Provider } from "@smithy/types"; /** * @internal */ type PreviouslyResolved = Partial<{ + credentials?: AwsCredentialIdentityProvider; accountIdEndpointMode?: Provider; }>; @@ -36,4 +41,14 @@ export async function checkFeatures( break; } } + + if (typeof config.credentials === "function") { + const credentials: AttributedAwsCredentialIdentity = await config.credentials?.(); + if (credentials.accountId) { + setFeature(context, "RESOLVED_ACCOUNT_ID", "T"); + } + for (const [key, value] of Object.entries(credentials.$source ?? {})) { + setFeature(context, key as keyof AwsSdkCredentialsFeatures, value); + } + } } diff --git a/packages/middleware-user-agent/src/middleware-user-agent.integ.spec.ts b/packages/middleware-user-agent/src/middleware-user-agent.integ.spec.ts index 4b83702638264..ca98caeec642b 100644 --- a/packages/middleware-user-agent/src/middleware-user-agent.integ.spec.ts +++ b/packages/middleware-user-agent/src/middleware-user-agent.integ.spec.ts @@ -1,6 +1,5 @@ import { CodeCatalyst } from "@aws-sdk/client-codecatalyst"; import { DynamoDB } from "@aws-sdk/client-dynamodb"; -import { S3 } from "@aws-sdk/client-s3"; import { DynamoDBDocument } from "@aws-sdk/lib-dynamodb"; import { requireRequestsFrom } from "../../../private/aws-util-test/src"; diff --git a/packages/types/src/feature-ids.ts b/packages/types/src/feature-ids.ts index 7dc1b9078aedf..6464a693651b6 100644 --- a/packages/types/src/feature-ids.ts +++ b/packages/types/src/feature-ids.ts @@ -21,7 +21,6 @@ export type AwsSdkFeatures = Partial<{ ACCOUNT_ID_MODE_DISABLED: "Q"; ACCOUNT_ID_MODE_REQUIRED: "R"; SIGV4A_SIGNING: "S"; - RESOLVED_ACCOUNT_ID: "T"; FLEXIBLE_CHECKSUMS_REQ_CRC32: "U"; FLEXIBLE_CHECKSUMS_REQ_CRC32C: "V"; FLEXIBLE_CHECKSUMS_REQ_CRC64: "W"; @@ -32,8 +31,15 @@ export type AwsSdkFeatures = Partial<{ FLEXIBLE_CHECKSUMS_RES_WHEN_SUPPORTED: "b"; FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED: "c"; DDB_MAPPER: "d"; +}> & + AwsSdkCredentialsFeatures; + +/** + * @internal + */ +export type AwsSdkCredentialsFeatures = Partial<{ + RESOLVED_ACCOUNT_ID: "T"; CREDENTIALS_CODE: "e"; - // CREDENTIALS_JVM_SYSTEM_PROPERTIES: "f"; // not applicable. CREDENTIALS_ENV_VARS: "g"; CREDENTIALS_ENV_VARS_STS_WEB_ID_TOKEN: "h"; CREDENTIALS_STS_ASSUME_ROLE: "i"; diff --git a/packages/types/src/identity/AwsCredentialIdentity.ts b/packages/types/src/identity/AwsCredentialIdentity.ts index 1113d9c37c040..813fd86f1128e 100644 --- a/packages/types/src/identity/AwsCredentialIdentity.ts +++ b/packages/types/src/identity/AwsCredentialIdentity.ts @@ -1 +1,9 @@ +import type { AwsCredentialIdentity } from "@smithy/types"; + +import type { AwsSdkCredentialsFeatures } from "../feature-ids"; + export { AwsCredentialIdentity, AwsCredentialIdentityProvider } from "@smithy/types"; + +export type AttributedAwsCredentialIdentity = AwsCredentialIdentity & { + $source?: AwsSdkCredentialsFeatures; +};