Skip to content

Commit 47818b6

Browse files
committed
resolving comments
1 parent b10ed4a commit 47818b6

File tree

3 files changed

+88
-58
lines changed

3 files changed

+88
-58
lines changed

packages/credential-providers/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"@aws-sdk/types": "*",
4545
"@smithy/core": "^3.1.5",
4646
"@smithy/credential-provider-imds": "^4.0.1",
47+
"@smithy/node-config-provider": "^4.0.1",
4748
"@smithy/property-provider": "^4.0.1",
4849
"@smithy/types": "^4.1.0",
4950
"tslib": "^2.6.2"
Lines changed: 66 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,59 +1,77 @@
1-
import { fromEnv } from "@aws-sdk/credential-provider-env";
2-
import { fromIni } from "@aws-sdk/credential-provider-ini";
3-
import { defaultProvider } from "@aws-sdk/credential-provider-node";
4-
import type { CredentialProviderOptions, Provider } from "@aws-sdk/types";
5-
import { chain, memoize } from "@smithy/property-provider";
6-
import type { AwsCredentialIdentity } from "@smithy/types";
1+
import { remoteProvider } from "@aws-sdk/credential-provider-node/src/remoteProvider";
2+
import { createCredentialChain } from "@aws-sdk/credential-providers";
3+
import type { RuntimeConfigAwsCredentialIdentityProvider } from "@aws-sdk/types";
4+
import type { AwsCredentialIdentity } from "@aws-sdk/types";
5+
import { CredentialsProviderError } from "@smithy/property-provider";
76

8-
interface AwsCliV2CompatibleProviderOptions extends CredentialProviderOptions {
7+
interface AwsCliV2CompatibleProviderOptions extends Partial<AwsCredentialIdentity> {
98
profile?: string;
10-
accessKeyId?: string;
11-
secretAccessKey?: string;
12-
sessionToken?: string;
9+
logger?: Console;
1310
}
1411

1512
/**
16-
* AWS CLI V2 Compatible Credential Provider Chain
17-
* If profile is explicitly provided, uses fromIni with that profile.
18-
* Otherwise, uses a chain of fromEnv and fromNodeProviderChain.
13+
* Custom AWS CLI V2 Compatible Credential Provider Chain.
14+
* Uses dynamic imports and `createCredentialChain` to mimic AWS CLI V2 behavior.
1915
*/
20-
export const fromAwsCliV2CompatibleProviderChain = (
21-
options: AwsCliV2CompatibleProviderOptions = {}
22-
): Provider<AwsCredentialIdentity> => {
23-
const { profile, accessKeyId, secretAccessKey, sessionToken } = options;
16+
export const fromAwsCliV2CompatibleProviderChain =
17+
(_init: AwsCliV2CompatibleProviderOptions = {}): RuntimeConfigAwsCredentialIdentityProvider =>
18+
async ({ callerClientConfig } = {}): Promise<AwsCredentialIdentity> => {
19+
// Merge init with caller's client config (profile/region).
20+
const init: AwsCliV2CompatibleProviderOptions = {
21+
..._init,
22+
...callerClientConfig,
23+
logger: (_init.logger ?? callerClientConfig?.logger ?? console) as Console,
24+
};
2425

25-
return memoize(
26-
async (): Promise<AwsCredentialIdentity> => {
27-
// If explicit credentials are provided in the constructor, use them.
28-
if (accessKeyId && secretAccessKey) {
29-
return {
30-
accessKeyId,
31-
secretAccessKey,
32-
sessionToken, // Optional
33-
};
34-
}
35-
// If profile is explicitly provided, use fromIni directly
36-
if (profile) {
37-
return fromIni({ profile })();
38-
}
26+
init.logger?.debug("@aws-sdk/custom-credential-chain - Initializing credential chain");
3927

40-
// Otherwise, use the chain of providers
41-
const credentials = await chain(fromEnv(), async () => {
42-
return defaultProvider()();
43-
})();
28+
const { profile, ...awsCredentials } = init;
4429

45-
if (!credentials) {
46-
throw new Error("Failed to retrieve valid AWS credentials");
47-
}
30+
// 1. If credentials are explicitly provided, return them.
31+
if (awsCredentials.accessKeyId && awsCredentials.secretAccessKey) {
32+
init.logger?.debug("@aws-sdk/custom-credential-chain - Using credentials from constructor");
33+
return awsCredentials as AwsCredentialIdentity;
34+
}
4835

49-
return credentials;
50-
},
51-
credentialsTreatedAsExpired,
52-
credentialsWillNeedRefresh
53-
);
54-
};
36+
// 2. If a profile is explicitly passed, use `fromIni`.
37+
if (profile) {
38+
init.logger?.debug("@aws-sdk/custom-credential-chain - Using fromIni with profile:", profile);
39+
const { fromIni } = await import("@aws-sdk/credential-provider-ini");
40+
return createCredentialChain(fromIni({ profile }))();
41+
}
5542

56-
export const credentialsTreatedAsExpired = (credentials: AwsCredentialIdentity) =>
57-
credentials?.expiration !== undefined && credentials.expiration.getTime() - Date.now() < 300000;
58-
59-
export const credentialsWillNeedRefresh = (credentials: AwsCredentialIdentity) => credentials?.expiration !== undefined;
43+
init.logger?.debug("@aws-sdk/cli-compatible-chain - Using from custom credential chain.");
44+
return createCredentialChain(
45+
async () => {
46+
init.logger?.debug("@aws-sdk/cli-compatible-chain - Trying fromEnv");
47+
const { fromEnv } = await import("@aws-sdk/credential-provider-env");
48+
return fromEnv()();
49+
},
50+
async () => {
51+
init.logger?.debug("@aws-sdk/cli-compatible-chain - Trying fromTokenFile");
52+
const { fromTokenFile } = await import("@aws-sdk/credential-provider-web-identity");
53+
return fromTokenFile()();
54+
},
55+
async () => {
56+
init.logger?.debug("@aws-sdk/cli-compatible-chain - Trying fromSSO");
57+
const { fromSSO } = await import("@aws-sdk/credential-provider-sso");
58+
return fromSSO()();
59+
},
60+
async () => {
61+
init.logger?.debug("@aws-sdk/cli-compatible-chain- Trying fromProcess");
62+
const { fromProcess } = await import("@aws-sdk/credential-provider-process");
63+
return fromProcess()();
64+
},
65+
async () => {
66+
init.logger?.debug("@aws-sdk/credential-provider-node - defaultProvider::remoteProvider");
67+
return (await remoteProvider(init))();
68+
},
69+
async () => {
70+
init.logger?.debug("@aws-sdk/custom-credential-chain - No valid credentials found. Throwing error.");
71+
throw new CredentialsProviderError("Could not load credentials from any providers", {
72+
tryNextLink: false,
73+
logger: init.logger,
74+
});
75+
}
76+
)();
77+
};

packages/credential-providers/src/resolveAwsCliV2Region.ts

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,50 @@
11
import { MetadataService } from "@aws-sdk/ec2-metadata-service";
2-
import { logger } from "@aws-sdk/smithy-client/dist-types";
3-
import { loadSharedConfigFiles } from "@smithy/shared-ini-file-loader";
2+
import { fromSharedConfigFiles } from "@smithy/node-config-provider";
3+
4+
interface ResolveRegionOptions {
5+
defaultRegion?: string;
6+
profile?: string;
7+
}
48

59
/**
610
* Resolves the AWS region following AWS CLI V2 precedence order.
711
*/
8-
export const resolveAwsCliV2Region = async (defaultRegion?: string, maybeProfile?: string): Promise<string> => {
9-
const profile = maybeProfile ?? process.env.AWS_PROFILE ?? process.env.AWS_DEFAULT_PROFILE ?? "default";
12+
export const resolveAwsCliV2Region = async ({
13+
defaultRegion,
14+
profile,
15+
}: ResolveRegionOptions): Promise<string | undefined> => {
16+
const resolvedProfile = profile ?? process.env.AWS_PROFILE ?? process.env.AWS_DEFAULT_PROFILE ?? "default";
1017

1118
const region =
1219
process.env.AWS_REGION ||
1320
process.env.AWS_DEFAULT_REGION ||
14-
(await getRegionFromIni(profile)) ||
21+
(await getRegionFromIni(resolvedProfile)) ||
1522
(await regionFromMetadataService());
1623

1724
if (!region) {
18-
const usedProfile = !profile ? "" : ` (profile: "${profile}")`;
25+
const usedProfile = resolvedProfile ? ` (profile: "${resolvedProfile}")` : "";
26+
1927
if (defaultRegion) {
20-
logger.warn(
28+
console.warn(
2129
`Unable to determine AWS region from environment or AWS configuration${usedProfile}, defaulting to '${defaultRegion}'`
2230
);
2331
return defaultRegion;
2432
}
25-
throw new Error(
26-
`Unable to determine AWS region from environment or AWS configuration${usedProfile}. Please specify a region.`
33+
34+
console.warn(
35+
`Unable to determine AWS region from environment or AWS configuration${usedProfile}. Returning undefined.`
2736
);
37+
return undefined;
2838
}
39+
2940
return region;
3041
};
3142

3243
/**
3344
* Fetches the region from the AWS shared config files.
3445
*/
3546
export async function getRegionFromIni(profile: string): Promise<string | undefined> {
36-
const sharedFiles = await loadSharedConfigFiles({ ignoreCache: true });
47+
const sharedFiles = await fromSharedConfigFiles({ ignoreCache: true });
3748
return sharedFiles.configFile?.[profile]?.region || sharedFiles.credentialsFile?.[profile]?.region;
3849
}
3950

0 commit comments

Comments
 (0)