Skip to content

Commit

Permalink
feat(credential-providers): lazy load STS client in credential providers
Browse files Browse the repository at this point in the history
  • Loading branch information
kuhe committed Jan 25, 2024
1 parent 40442fb commit c0db7e0
Show file tree
Hide file tree
Showing 43 changed files with 207 additions and 2,740 deletions.
27 changes: 16 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@ unlink-smithy:
copy-smithy:
node ./scripts/copy-smithy-dist-files

gen-auth:
node ./scripts/cli-dispatcher client sso - gen;
node ./scripts/cli-dispatcher client sts - gen;
node ./scripts/cli-dispatcher client sso-oidc - gen;
node ./scripts/cli-dispatcher client cognito identity - gen;

b-auth:
node ./scripts/cli-dispatcher client sso - deps;
node ./scripts/cli-dispatcher client sts - b;
node ./scripts/cli-dispatcher client sso-oidc - b;
node ./scripts/cli-dispatcher client cognito identity - b;

# Runs build for all packages using Turborepo
turbo-build:
(cd scripts/remote-cache && yarn)
Expand All @@ -31,17 +43,10 @@ turbo-build:
npx turbo run build --api="http://localhost:3000" --team="aws-sdk-js" --token="xyz"
node scripts/remote-cache/ stop

protocols:
yarn generate-clients -g codegen/sdk-codegen/aws-models/rekognitionstreaming.json
git checkout HEAD clients/client-rekognitionstreaming
yarn test:protocols
# run turbo build for packages only.
tpk:
npx turbo run build --filter='./packages/*'

server-protocols:
yarn generate-clients -s
yarn test:server-protocols

bytes-cjs:
make turbo-build
node scripts/remote-cache/ start&
npx turbo run build:cjs --api="http://localhost:3000" --team="aws-sdk-js" --token="xyz"
node scripts/remote-cache/ stop
yarn test:server-protocols
4 changes: 1 addition & 3 deletions clients/client-s3/src/runtimeConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// @ts-ignore: package.json will be imported from dist folders
import packageInfo from "../package.json"; // eslint-disable-line

import { decorateDefaultCredentialProvider } from "@aws-sdk/client-sts";
import { emitWarningIfUnsupportedVersion as awsCheckVersion } from "@aws-sdk/core";
import { defaultProvider as credentialDefaultProvider } from "@aws-sdk/credential-provider-node";
import { NODE_USE_ARN_REGION_CONFIG_OPTIONS } from "@aws-sdk/middleware-bucket-endpoint";
Expand Down Expand Up @@ -44,8 +43,7 @@ export const getRuntimeConfig = (config: S3ClientConfig) => {
runtime: "node",
defaultsMode,
bodyLengthChecker: config?.bodyLengthChecker ?? calculateBodyLength,
credentialDefaultProvider:
config?.credentialDefaultProvider ?? decorateDefaultCredentialProvider(credentialDefaultProvider),
credentialDefaultProvider: config?.credentialDefaultProvider ?? credentialDefaultProvider,
defaultUserAgentProvider:
config?.defaultUserAgentProvider ??
defaultUserAgent({ serviceId: clientSharedValues.serviceId, clientVersion: packageInfo.version }),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,19 +207,22 @@ public Map<String, Consumer<TypeScriptWriter>> getRuntimeConfigWriters(
case NODE:
return MapUtils.of(
"credentialDefaultProvider", writer -> {
if (!testServiceId(service, "STS")) {
writer.addDependency(AwsDependency.STS_CLIENT);
writer.addImport("decorateDefaultCredentialProvider", "decorateDefaultCredentialProvider",
AwsDependency.STS_CLIENT);
if (testServiceId(service, "STS")) {
writer
.addRelativeImport("decorateDefaultCredentialProvider", null,
Paths.get(".", CodegenUtils.SOURCE_FOLDER, STS_ROLE_ASSUMERS_FILE))
.addDependency(AwsDependency.CREDENTIAL_PROVIDER_NODE)
.addImport("defaultProvider", "credentialDefaultProvider",
AwsDependency.CREDENTIAL_PROVIDER_NODE)
.write("decorateDefaultCredentialProvider(credentialDefaultProvider)");
} else {
writer.addRelativeImport("decorateDefaultCredentialProvider",
"decorateDefaultCredentialProvider", Paths.get(".", CodegenUtils.SOURCE_FOLDER,
STS_ROLE_ASSUMERS_FILE));
writer
.addDependency(AwsDependency.STS_CLIENT)
.addDependency(AwsDependency.CREDENTIAL_PROVIDER_NODE)
.addImport("defaultProvider", "credentialDefaultProvider",
AwsDependency.CREDENTIAL_PROVIDER_NODE)
.write("credentialDefaultProvider");
}
writer.addDependency(AwsDependency.CREDENTIAL_PROVIDER_NODE);
writer.addImport("defaultProvider", "credentialDefaultProvider",
AwsDependency.CREDENTIAL_PROVIDER_NODE);
writer.write("decorateDefaultCredentialProvider(credentialDefaultProvider)");
}
);
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import java.util.logging.Logger;
import software.amazon.smithy.aws.traits.ServiceTrait;
import software.amazon.smithy.aws.traits.auth.SigV4Trait;
import software.amazon.smithy.aws.typescript.codegen.AddAwsAuthPlugin;
import software.amazon.smithy.codegen.core.SymbolProvider;
import software.amazon.smithy.model.Model;
import software.amazon.smithy.model.shapes.ServiceShape;
Expand All @@ -27,7 +26,7 @@
*/
@SmithyInternalApi
public final class AddAwsDefaultSigningName implements TypeScriptIntegration {
private static final Logger LOGGER = Logger.getLogger(AddAwsAuthPlugin.class.getName());
private static final Logger LOGGER = Logger.getLogger(AddAwsDefaultSigningName.class.getName());

/**
* Integration should only be used if `experimentalIdentityAndAuth` flag is true.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,12 +112,10 @@ public Map<String, Consumer<TypeScriptWriter>> getRuntimeConfigWriters(
"credentialDefaultProvider", writer -> {
writer
.addDependency(AwsDependency.STS_CLIENT)
.addImport("decorateDefaultCredentialProvider", "decorateDefaultCredentialProvider",
AwsDependency.STS_CLIENT)
.addDependency(AwsDependency.CREDENTIAL_PROVIDER_NODE)
.addImport("defaultProvider", "credentialDefaultProvider",
AwsDependency.CREDENTIAL_PROVIDER_NODE)
.write("decorateDefaultCredentialProvider(credentialDefaultProvider)");
.write("credentialDefaultProvider");
}
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,6 @@ public void awsClient() {
assertThat(manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/NotSameClient.ts").get(), not(containsString("signingName")));

// Check config files
assertThat(manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/runtimeConfig.ts").get(),
containsString("decorateDefaultCredentialProvider"));
assertThat(manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/runtimeConfig.browser.ts").get(), containsString("Credential is missing"));
assertThat(manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/runtimeConfig.shared.ts").get(), not(containsString("signingName:")));

Expand Down Expand Up @@ -81,8 +79,6 @@ public void sigV4GenericClient() {
assertThat(manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/SsdkExampleSigV4Client.ts").get(), containsString("signingName?"));

// Check config files
assertThat(manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/runtimeConfig.ts").get(),
containsString("decorateDefaultCredentialProvider"));
assertThat(manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/runtimeConfig.browser.ts").get(), containsString("Credential is missing"));
assertThat(manifest.getFileString(CodegenUtils.SOURCE_FOLDER + "/runtimeConfig.shared.ts").get(), containsString("signingName:"));

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@
"ts-loader": "9.4.2",
"ts-mocha": "10.0.0",
"ts-node": "10.9.1",
"turbo": "^1.6.3",
"turbo": "^1.11.3",
"typescript": "~4.9.5",
"verdaccio": "5.25.0",
"webpack": "5.73.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CognitoIdentityClient } from "@aws-sdk/client-cognito-identity";
import type { CognitoIdentityClient } from "@aws-sdk/client-cognito-identity";

import { Logins } from "./Logins";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { GetCredentialsForIdentityCommand } from "@aws-sdk/client-cognito-identity";
import { CredentialsProviderError } from "@smithy/property-provider";
import { AwsCredentialIdentity, Provider } from "@smithy/types";

Expand Down Expand Up @@ -30,6 +29,8 @@ export type CognitoIdentityCredentialProvider = Provider<CognitoIdentityCredenti
*/
export function fromCognitoIdentity(parameters: FromCognitoIdentityParameters): CognitoIdentityCredentialProvider {
return async (): Promise<CognitoIdentityCredentials> => {
const { GetCredentialsForIdentityCommand } = await import("./loadCognitoIdentity");

const {
Credentials: {
AccessKeyId = throwOnMissingAccessKeyId(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { GetIdCommand } from "@aws-sdk/client-cognito-identity";
import { CredentialsProviderError } from "@smithy/property-provider";

import { CognitoProviderParameters } from "./CognitoProviderParameters";
Expand Down Expand Up @@ -26,11 +25,15 @@ export function fromCognitoIdentityPool({
logins,
userIdentifier = !logins || Object.keys(logins).length === 0 ? "ANONYMOUS" : undefined,
}: FromCognitoIdentityPoolParameters): CognitoIdentityCredentialProvider {
const cacheKey = userIdentifier ? `aws:cognito-identity-credentials:${identityPoolId}:${userIdentifier}` : undefined;
const cacheKey: string | undefined = userIdentifier
? `aws:cognito-identity-credentials:${identityPoolId}:${userIdentifier}`
: undefined;

let provider: CognitoIdentityCredentialProvider = async () => {
let identityId = cacheKey && (await cache.getItem(cacheKey));
if (!identityId) {
const { GetIdCommand } = await import("./loadCognitoIdentity");

const { IdentityId = throwOnMissingId() } = await client.send(
new GetIdCommand({
AccountId: accountId,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { GetCredentialsForIdentityCommand, GetIdCommand } from "@aws-sdk/client-cognito-identity";

// This file must be loaded dynamically.
export { GetCredentialsForIdentityCommand, GetIdCommand };
3 changes: 3 additions & 0 deletions packages/credential-provider-ini/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
"@smithy/types": "^2.9.1",
"tslib": "^2.5.0"
},
"peerDependencies": {
"@aws-sdk/client-sts": "*"
},
"devDependencies": {
"@tsconfig/recommended": "1.0.1",
"@types/node": "^14.14.31",
Expand Down
8 changes: 6 additions & 2 deletions packages/credential-provider-ini/src/fromIni.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import type { STSClientConfig } from "@aws-sdk/client-sts";
import { AssumeRoleWithWebIdentityParams } from "@aws-sdk/credential-provider-web-identity";
import { getProfileName, parseKnownFiles, SourceProfileInit } from "@smithy/shared-ini-file-loader";
import { AwsCredentialIdentity, AwsCredentialIdentityProvider } from "@smithy/types";
import type { AwsCredentialIdentity, AwsCredentialIdentityProvider, Pluggable } from "@smithy/types";

import { AssumeRoleParams } from "./resolveAssumeRoleCredentials";
import { resolveProfileData } from "./resolveProfileData";

/**
* @internal
* @public
*/
export interface FromIniInit extends SourceProfileInit {
/**
Expand Down Expand Up @@ -36,6 +37,9 @@ export interface FromIniInit extends SourceProfileInit {
* @param params
*/
roleAssumerWithWebIdentity?: (params: AssumeRoleWithWebIdentityParams) => Promise<AwsCredentialIdentity>;

clientConfig?: STSClientConfig;
clientPlugins?: Pluggable<any, any>[];
}

/**
Expand Down
4 changes: 4 additions & 0 deletions packages/credential-provider-ini/src/loadSts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { getDefaultRoleAssumer } from "@aws-sdk/client-sts";

// This file must be loaded dynamically.
export { getDefaultRoleAssumer };
Original file line number Diff line number Diff line change
Expand Up @@ -86,10 +86,8 @@ export const resolveAssumeRoleCredentials = async (
const data = profiles[profileName];

if (!options.roleAssumer) {
throw new CredentialsProviderError(
`Profile ${profileName} requires a role to be assumed, but no role assumption callback was provided.`,
false
);
const { getDefaultRoleAssumer } = await import("./loadSts");
options.roleAssumer = getDefaultRoleAssumer(options.clientConfig, options.clientPlugins);
}

const { source_profile } = data;
Expand Down
27 changes: 15 additions & 12 deletions packages/credential-provider-node/src/defaultProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import { AwsCredentialIdentity, MemoizedProvider } from "@smithy/types";

import { remoteProvider } from "./remoteProvider";

/**
* @public
*/
export type DefaultProviderInit = FromIniInit & RemoteProviderInit & FromProcessInit & FromSSOInit & FromTokenFileInit;

/**
Expand All @@ -30,20 +33,20 @@ export type DefaultProviderInit = FromIniInit & RemoteProviderInit & FromProcess
* @param init Configuration that is passed to each individual
* provider
*
* @see {@link fromEnv} The function used to source credentials from
* environment variables
* @see {@link fromSSO} The function used to source credentials from
* resolved SSO token cache
* @see {@link fromTokenFile} The function used to source credentials from
* token file
* @see {@link fromIni} The function used to source credentials from INI
* files
* @see {@link fromProcess} The function used to sources credentials from
* credential_process in INI files
* @see {@link fromEnv} The function used to source credentials from
* environment variables.
* @see {@link fromSSO} The function used to source credentials from
* resolved SSO token cache.
* @see {@link fromTokenFile} The function used to source credentials from
* token file.
* @see {@link fromIni} The function used to source credentials from INI
* files.
* @see {@link fromProcess} The function used to sources credentials from
* credential_process in INI files.
* @see {@link fromInstanceMetadata} The function used to source credentials from the
* EC2 Instance Metadata Service
* EC2 Instance Metadata Service.
* @see {@link fromContainerMetadata} The function used to source credentials from the
* ECS Container Metadata Service
* ECS Container Metadata Service.
*/
export const defaultProvider = (init: DefaultProviderInit = {}): MemoizedProvider<AwsCredentialIdentity> =>
memoize(
Expand Down
10 changes: 8 additions & 2 deletions packages/credential-provider-sso/src/fromSSO.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { SSOClient } from "@aws-sdk/client-sso";
import type { SSOClient, SSOClientConfig } from "@aws-sdk/client-sso";
import { CredentialsProviderError } from "@smithy/property-provider";
import { getProfileName, loadSsoSessionData, parseKnownFiles, SourceProfileInit } from "@smithy/shared-ini-file-loader";
import { AwsCredentialIdentityProvider } from "@smithy/types";
Expand Down Expand Up @@ -43,6 +43,7 @@ export interface SsoCredentialsParameters {
*/
export interface FromSSOInit extends SourceProfileInit {
ssoClient?: SSOClient;
clientConfig?: SSOClientConfig;
}

/**
Expand Down Expand Up @@ -79,7 +80,12 @@ export interface FromSSOInit extends SourceProfileInit {
export const fromSSO =
(init: FromSSOInit & Partial<SsoCredentialsParameters> = {}): AwsCredentialIdentityProvider =>
async () => {
const { ssoStartUrl, ssoAccountId, ssoRegion, ssoRoleName, ssoClient, ssoSession } = init;
const { ssoStartUrl, ssoAccountId, ssoRegion, ssoRoleName, ssoSession } = init;
let { ssoClient } = init;
if (!ssoClient) {
const { SSOClient } = await import("./loadSso");
ssoClient = new SSOClient(init.clientConfig ?? {});
}
const profileName = getProfileName(init);

if (!ssoStartUrl && !ssoAccountId && !ssoRegion && !ssoRoleName && !ssoSession) {
Expand Down
4 changes: 4 additions & 0 deletions packages/credential-provider-sso/src/loadSso.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
import { GetRoleCredentialsCommand, SSOClient } from "@aws-sdk/client-sso";

// This file must be loaded dynamically.
export { GetRoleCredentialsCommand, SSOClient };
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { GetRoleCredentialsCommand, GetRoleCredentialsCommandOutput, SSOClient } from "@aws-sdk/client-sso";
import type { GetRoleCredentialsCommandOutput } from "@aws-sdk/client-sso";
import { fromSso as getSsoTokenProvider } from "@aws-sdk/token-providers";
import { CredentialsProviderError } from "@smithy/property-provider";
import { getSSOTokenFromFile, SSOToken } from "@smithy/shared-ini-file-loader";
Expand Down Expand Up @@ -52,6 +52,9 @@ export const resolveSSOCredentials = async ({
}

const { accessToken } = token;

const { SSOClient, GetRoleCredentialsCommand } = await import("./loadSso");

const sso = ssoClient || new SSOClient({ region: ssoRegion });
let ssoResp: GetRoleCredentialsCommandOutput;
try {
Expand Down
3 changes: 3 additions & 0 deletions packages/credential-provider-web-identity/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
"@smithy/types": "^2.9.1",
"tslib": "^2.5.0"
},
"peerDependencies": {
"@aws-sdk/client-sts": "*"
},
"devDependencies": {
"@tsconfig/recommended": "1.0.1",
"@types/node": "^14.14.31",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const ENV_ROLE_ARN = "AWS_ROLE_ARN";
const ENV_ROLE_SESSION_NAME = "AWS_ROLE_SESSION_NAME";

/**
* @internal
* @public
*/
export interface FromTokenFileInit extends Partial<Omit<FromWebTokenInit, "webIdentityToken">> {
/**
Expand Down
Loading

0 comments on commit c0db7e0

Please sign in to comment.