-
Notifications
You must be signed in to change notification settings - Fork 598
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
133 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
59 changes: 59 additions & 0 deletions
59
packages/credential-providers/src/fromAwsCliV2CompatibleProviderChain.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import { fromEnv } from "@aws-sdk/credential-provider-env"; | ||
import { fromIni } from "@aws-sdk/credential-provider-ini"; | ||
import { defaultProvider } from "@aws-sdk/credential-provider-node"; | ||
import type { CredentialProviderOptions, Provider } from "@aws-sdk/types"; | ||
import { chain, memoize } from "@smithy/property-provider"; | ||
import type { AwsCredentialIdentity } from "@smithy/types"; | ||
|
||
interface AwsCliV2CompatibleProviderOptions extends CredentialProviderOptions { | ||
profile?: string; | ||
accessKeyId?: string; | ||
secretAccessKey?: string; | ||
sessionToken?: string; | ||
} | ||
|
||
/** | ||
* AWS CLI V2 Compatible Credential Provider Chain | ||
* If profile is explicitly provided, uses fromIni with that profile. | ||
* Otherwise, uses a chain of fromEnv and fromNodeProviderChain. | ||
*/ | ||
export const fromAwsCliV2CompatibleProviderChain = ( | ||
options: AwsCliV2CompatibleProviderOptions = {} | ||
): Provider<AwsCredentialIdentity> => { | ||
const { profile, accessKeyId, secretAccessKey, sessionToken } = options; | ||
|
||
return memoize( | ||
async (): Promise<AwsCredentialIdentity> => { | ||
// If explicit credentials are provided in the constructor, use them. | ||
if (accessKeyId && secretAccessKey) { | ||
return { | ||
accessKeyId, | ||
secretAccessKey, | ||
sessionToken, // Optional | ||
}; | ||
} | ||
// If profile is explicitly provided, use fromIni directly | ||
if (profile) { | ||
return fromIni({ profile })(); | ||
} | ||
|
||
// Otherwise, use the chain of providers | ||
const credentials = await chain(fromEnv(), async () => { | ||
return defaultProvider()(); | ||
})(); | ||
|
||
if (!credentials) { | ||
throw new Error("Failed to retrieve valid AWS credentials"); | ||
} | ||
|
||
return credentials; | ||
}, | ||
credentialsTreatedAsExpired, | ||
credentialsWillNeedRefresh | ||
); | ||
}; | ||
|
||
export const credentialsTreatedAsExpired = (credentials: AwsCredentialIdentity) => | ||
credentials?.expiration !== undefined && credentials.expiration.getTime() - Date.now() < 300000; | ||
|
||
export const credentialsWillNeedRefresh = (credentials: AwsCredentialIdentity) => credentials?.expiration !== undefined; |
51 changes: 51 additions & 0 deletions
51
packages/credential-providers/src/resolveAwsCliV2Region.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { MetadataService } from "@aws-sdk/ec2-metadata-service"; | ||
import { logger } from "@aws-sdk/smithy-client/dist-types"; | ||
import { loadSharedConfigFiles } from "@smithy/shared-ini-file-loader"; | ||
|
||
/** | ||
* Resolves the AWS region following AWS CLI V2 precedence order. | ||
*/ | ||
export const resolveAwsCliV2Region = async (defaultRegion?: string, maybeProfile?: string): Promise<string> => { | ||
const profile = maybeProfile ?? process.env.AWS_PROFILE ?? process.env.AWS_DEFAULT_PROFILE ?? "default"; | ||
|
||
const region = | ||
process.env.AWS_REGION || | ||
process.env.AWS_DEFAULT_REGION || | ||
(await getRegionFromIni(profile)) || | ||
(await regionFromMetadataService()); | ||
|
||
if (!region) { | ||
const usedProfile = !profile ? "" : ` (profile: "${profile}")`; | ||
if (defaultRegion) { | ||
logger.warn( | ||
`Unable to determine AWS region from environment or AWS configuration${usedProfile}, defaulting to '${defaultRegion}'` | ||
); | ||
return defaultRegion; | ||
} | ||
throw new Error( | ||
`Unable to determine AWS region from environment or AWS configuration${usedProfile}. Please specify a region.` | ||
); | ||
} | ||
return region; | ||
}; | ||
|
||
/** | ||
* Fetches the region from the AWS shared config files. | ||
*/ | ||
export async function getRegionFromIni(profile: string): Promise<string | undefined> { | ||
const sharedFiles = await loadSharedConfigFiles({ ignoreCache: true }); | ||
return sharedFiles.configFile?.[profile]?.region || sharedFiles.credentialsFile?.[profile]?.region; | ||
} | ||
|
||
/** | ||
* Retrieves the AWS region from the EC2 Instance Metadata Service (IMDS). | ||
*/ | ||
export async function regionFromMetadataService(): Promise<string | undefined> { | ||
try { | ||
const metadataService = new MetadataService(); | ||
const document = await metadataService.request("/latest/dynamic/instance-identity/document", {}); | ||
return JSON.parse(document).region; | ||
} catch (e) { | ||
return undefined; | ||
} | ||
} |