1- import * as crypto from 'crypto' ;
21import * as process from 'process' ;
3- import { promisify } from 'util' ;
42
53import type { Binary , BSONSerializeOptions } from '../../bson' ;
64import * as BSON from '../../bson' ;
7- import { aws4 , getAwsCredentialProvider } from '../../deps' ;
5+ import { aws4 , type AWSCredentials , getAwsCredentialProvider } from '../../deps' ;
86import {
97 MongoAWSError ,
108 MongoCompatibilityError ,
119 MongoMissingCredentialsError ,
1210 MongoRuntimeError
1311} from '../../error' ;
14- import { ByteUtils , maxWireVersion , ns , request } from '../../utils' ;
12+ import { ByteUtils , maxWireVersion , ns , randomBytes , request } from '../../utils' ;
1513import { type AuthContext , AuthProvider } from './auth_provider' ;
1614import { MongoCredentials } from './mongo_credentials' ;
1715import { AuthMechanism } from './providers' ;
@@ -57,12 +55,40 @@ interface AWSSaslContinuePayload {
5755}
5856
5957export class MongoDBAWS extends AuthProvider {
60- static credentialProvider : ReturnType < typeof getAwsCredentialProvider > | null = null ;
61- randomBytesAsync : ( size : number ) => Promise < Buffer > ;
58+ static credentialProvider : ReturnType < typeof getAwsCredentialProvider > ;
59+ provider ? : ( ) => Promise < AWSCredentials > ;
6260
6361 constructor ( ) {
6462 super ( ) ;
65- this . randomBytesAsync = promisify ( crypto . randomBytes ) ;
63+ MongoDBAWS . credentialProvider ??= getAwsCredentialProvider ( ) ;
64+
65+ let { AWS_STS_REGIONAL_ENDPOINTS = '' , AWS_REGION = '' } = process . env ;
66+ AWS_STS_REGIONAL_ENDPOINTS = AWS_STS_REGIONAL_ENDPOINTS . toLowerCase ( ) ;
67+ AWS_REGION = AWS_REGION . toLowerCase ( ) ;
68+
69+ /** The option setting should work only for users who have explicit settings in their environment, the driver should not encode "defaults" */
70+ const awsRegionSettingsExist =
71+ AWS_REGION . length !== 0 && AWS_STS_REGIONAL_ENDPOINTS . length !== 0 ;
72+
73+ /**
74+ * If AWS_STS_REGIONAL_ENDPOINTS is set to regional, users are opting into the new behavior of respecting the region settings
75+ *
76+ * If AWS_STS_REGIONAL_ENDPOINTS is set to legacy, then "old" regions need to keep using the global setting.
77+ * Technically the SDK gets this wrong, it reaches out to 'sts.us-east-1.amazonaws.com' when it should be 'sts.amazonaws.com'.
78+ * That is not our bug to fix here. We leave that up to the SDK.
79+ */
80+ const useRegionalSts =
81+ AWS_STS_REGIONAL_ENDPOINTS === 'regional' ||
82+ ( AWS_STS_REGIONAL_ENDPOINTS === 'legacy' && ! LEGACY_REGIONS . has ( AWS_REGION ) ) ;
83+
84+ if ( 'fromNodeProviderChain' in MongoDBAWS . credentialProvider ) {
85+ this . provider =
86+ awsRegionSettingsExist && useRegionalSts
87+ ? MongoDBAWS . credentialProvider . fromNodeProviderChain ( {
88+ clientConfig : { region : AWS_REGION }
89+ } )
90+ : MongoDBAWS . credentialProvider . fromNodeProviderChain ( ) ;
91+ }
6692 }
6793
6894 override async auth ( authContext : AuthContext ) : Promise < void > {
@@ -83,7 +109,7 @@ export class MongoDBAWS extends AuthProvider {
83109 }
84110
85111 if ( ! authContext . credentials . username ) {
86- authContext . credentials = await makeTempCredentials ( authContext . credentials ) ;
112+ authContext . credentials = await makeTempCredentials ( authContext . credentials , this . provider ) ;
87113 }
88114
89115 const { credentials } = authContext ;
@@ -101,7 +127,7 @@ export class MongoDBAWS extends AuthProvider {
101127 : undefined ;
102128
103129 const db = credentials . source ;
104- const nonce = await this . randomBytesAsync ( 32 ) ;
130+ const nonce = await randomBytes ( 32 ) ;
105131
106132 const saslStart = {
107133 saslStart : 1 ,
@@ -181,7 +207,10 @@ interface AWSTempCredentials {
181207 Expiration ?: Date ;
182208}
183209
184- async function makeTempCredentials ( credentials : MongoCredentials ) : Promise < MongoCredentials > {
210+ async function makeTempCredentials (
211+ credentials : MongoCredentials ,
212+ provider ?: ( ) => Promise < AWSCredentials >
213+ ) : Promise < MongoCredentials > {
185214 function makeMongoCredentialsFromAWSTemp ( creds : AWSTempCredentials ) {
186215 if ( ! creds . AccessKeyId || ! creds . SecretAccessKey || ! creds . Token ) {
187216 throw new MongoMissingCredentialsError ( 'Could not obtain temporary MONGODB-AWS credentials' ) ;
@@ -198,11 +227,31 @@ async function makeTempCredentials(credentials: MongoCredentials): Promise<Mongo
198227 } ) ;
199228 }
200229
201- MongoDBAWS . credentialProvider ??= getAwsCredentialProvider ( ) ;
202-
203230 // Check if the AWS credential provider from the SDK is present. If not,
204231 // use the old method.
205- if ( 'kModuleError' in MongoDBAWS . credentialProvider ) {
232+ if ( provider && ! ( 'kModuleError' in MongoDBAWS . credentialProvider ) ) {
233+ /*
234+ * Creates a credential provider that will attempt to find credentials from the
235+ * following sources (listed in order of precedence):
236+ *
237+ * - Environment variables exposed via process.env
238+ * - SSO credentials from token cache
239+ * - Web identity token credentials
240+ * - Shared credentials and config ini files
241+ * - The EC2/ECS Instance Metadata Service
242+ */
243+ try {
244+ const creds = await provider ( ) ;
245+ return makeMongoCredentialsFromAWSTemp ( {
246+ AccessKeyId : creds . accessKeyId ,
247+ SecretAccessKey : creds . secretAccessKey ,
248+ Token : creds . sessionToken ,
249+ Expiration : creds . expiration
250+ } ) ;
251+ } catch ( error ) {
252+ throw new MongoAWSError ( error . message ) ;
253+ }
254+ } else {
206255 // If the environment variable AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
207256 // is set then drivers MUST assume that it was set by an AWS ECS agent
208257 if ( process . env . AWS_CONTAINER_CREDENTIALS_RELATIVE_URI ) {
@@ -232,54 +281,6 @@ async function makeTempCredentials(credentials: MongoCredentials): Promise<Mongo
232281 } ) ;
233282
234283 return makeMongoCredentialsFromAWSTemp ( creds ) ;
235- } else {
236- let { AWS_STS_REGIONAL_ENDPOINTS = '' , AWS_REGION = '' } = process . env ;
237- AWS_STS_REGIONAL_ENDPOINTS = AWS_STS_REGIONAL_ENDPOINTS . toLowerCase ( ) ;
238- AWS_REGION = AWS_REGION . toLowerCase ( ) ;
239-
240- /** The option setting should work only for users who have explicit settings in their environment, the driver should not encode "defaults" */
241- const awsRegionSettingsExist =
242- AWS_REGION . length !== 0 && AWS_STS_REGIONAL_ENDPOINTS . length !== 0 ;
243-
244- /**
245- * If AWS_STS_REGIONAL_ENDPOINTS is set to regional, users are opting into the new behavior of respecting the region settings
246- *
247- * If AWS_STS_REGIONAL_ENDPOINTS is set to legacy, then "old" regions need to keep using the global setting.
248- * Technically the SDK gets this wrong, it reaches out to 'sts.us-east-1.amazonaws.com' when it should be 'sts.amazonaws.com'.
249- * That is not our bug to fix here. We leave that up to the SDK.
250- */
251- const useRegionalSts =
252- AWS_STS_REGIONAL_ENDPOINTS === 'regional' ||
253- ( AWS_STS_REGIONAL_ENDPOINTS === 'legacy' && ! LEGACY_REGIONS . has ( AWS_REGION ) ) ;
254-
255- const provider =
256- awsRegionSettingsExist && useRegionalSts
257- ? MongoDBAWS . credentialProvider . fromNodeProviderChain ( {
258- clientConfig : { region : AWS_REGION }
259- } )
260- : MongoDBAWS . credentialProvider . fromNodeProviderChain ( ) ;
261-
262- /*
263- * Creates a credential provider that will attempt to find credentials from the
264- * following sources (listed in order of precedence):
265- *
266- * - Environment variables exposed via process.env
267- * - SSO credentials from token cache
268- * - Web identity token credentials
269- * - Shared credentials and config ini files
270- * - The EC2/ECS Instance Metadata Service
271- */
272- try {
273- const creds = await provider ( ) ;
274- return makeMongoCredentialsFromAWSTemp ( {
275- AccessKeyId : creds . accessKeyId ,
276- SecretAccessKey : creds . secretAccessKey ,
277- Token : creds . sessionToken ,
278- Expiration : creds . expiration
279- } ) ;
280- } catch ( error ) {
281- throw new MongoAWSError ( error . message ) ;
282- }
283284 }
284285}
285286
0 commit comments