From 381a5f3f46c7932a68b5dd57bab5138f3eb7aae8 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 9 Sep 2025 14:04:44 -0400 Subject: [PATCH 1/6] feat(NODE-7047): use custom credential provider first --- test/integration/auth/mongodb_aws.test.ts | 134 ++++++++++++++---- ...26.custom_aws_credential_providers.test.ts | 45 ++++++ 2 files changed, 151 insertions(+), 28 deletions(-) diff --git a/test/integration/auth/mongodb_aws.test.ts b/test/integration/auth/mongodb_aws.test.ts index d7153cc1d86..d6efe7bcda5 100644 --- a/test/integration/auth/mongodb_aws.test.ts +++ b/test/integration/auth/mongodb_aws.test.ts @@ -163,39 +163,117 @@ describe('MONGODB-AWS', function () { }); }); - context('when user supplies a credentials provider', function () { - let providerCount = 0; + context('when using a custom credential provider', function () { + context('1. Custom Credential Provider Authenticates', function () { + let providerCount = 0; - beforeEach(function () { - // If we have a username the credentials have been set from the URI, options, or environment - // variables per the auth spec stated order. - if (client.options.credentials.username) { - this.skipReason = 'Credentials in the URI on env variables will not use custom provider.'; - return this.skip(); - } + beforeEach(function () { + // If we have a username the credentials have been set from the URI, options, or environment + // variables per the auth spec stated order. + if (client.options.credentials.username) { + this.skipReason = 'Credentials in the URI will not use custom provider.'; + return this.skip(); + } + }); + + it('authenticates with a user provided credentials provider', async function () { + const credentialProvider = AWSSDKCredentialProvider.awsSDK; + const provider = async () => { + providerCount++; + return await credentialProvider.fromNodeProviderChain().apply(); + }; + client = this.configuration.newClient(process.env.MONGODB_URI, { + authMechanismProperties: { + AWS_CREDENTIAL_PROVIDER: provider + } + }); + + const result = await client + .db('aws') + .collection('aws_test') + .estimatedDocumentCount() + .catch(error => error); + + expect(result).to.not.be.instanceOf(MongoServerError); + expect(result).to.be.a('number'); + expect(providerCount).to.be.greaterThan(0); + }); }); - it('authenticates with a user provided credentials provider', async function () { - const credentialProvider = AWSSDKCredentialProvider.awsSDK; - const provider = async () => { - providerCount++; - return await credentialProvider.fromNodeProviderChain().apply(); - }; - client = this.configuration.newClient(process.env.MONGODB_URI, { - authMechanismProperties: { - AWS_CREDENTIAL_PROVIDER: provider - } + context('2. Custom Credential Provider Authentication Precedence', function () { + context('Case 1: Credentials in URI Take Precedence', function () { + let providerCount = 0; + let provider; + + beforeEach(function () { + console.log(client?.options); + if (!client?.options.credentials.username) { + this.skipReason = 'Test only runs when credentials are present in the URI'; + return this.skip(); + } + // @ts-expect-error We intentionally access a protected variable. + const credentialProvider = AWSTemporaryCredentialProvider.awsSDK; + provider = async () => { + providerCount++; + return await credentialProvider.fromNodeProviderChain().apply(); + }; + }); + + it('authenticates with a user provided credentials provider', async function () { + console.log(process.env); + client = this.configuration.newClient(process.env.MONGODB_URI, { + authMechanismProperties: { + AWS_CREDENTIAL_PROVIDER: provider + } + }); + + const result = await client + .db('aws') + .collection('aws_test') + .estimatedDocumentCount() + .catch(error => error); + + expect(result).to.not.be.instanceOf(MongoServerError); + expect(result).to.be.a('number'); + expect(providerCount).to.equal(0); + }); }); - const result = await client - .db('aws') - .collection('aws_test') - .estimatedDocumentCount() - .catch(error => error); + context('Case 2: Custom Provider Takes Precedence Over Environment Variables', function () { + let providerCount = 0; + let provider; - expect(result).to.not.be.instanceOf(MongoServerError); - expect(result).to.be.a('number'); - expect(providerCount).to.be.greaterThan(0); + beforeEach(function () { + if (client?.options.credentials.username || !process.env.AWS_ACCESS_KEY_ID) { + this.skipReason = 'Test only runs when credentials are present in the environment'; + return this.skip(); + } + // @ts-expect-error We intentionally access a protected variable. + const credentialProvider = AWSTemporaryCredentialProvider.awsSDK; + provider = async () => { + providerCount++; + return await credentialProvider.fromNodeProviderChain().apply(); + }; + }); + + it('authenticates with a user provided credentials provider', async function () { + client = this.configuration.newClient(process.env.MONGODB_URI, { + authMechanismProperties: { + AWS_CREDENTIAL_PROVIDER: provider + } + }); + + const result = await client + .db('aws') + .collection('aws_test') + .estimatedDocumentCount() + .catch(error => error); + + expect(result).to.not.be.instanceOf(MongoServerError); + expect(result).to.be.a('number'); + expect(providerCount).to.be.greaterThan(0); + }); + }); }); }); @@ -218,7 +296,7 @@ describe('MONGODB-AWS', function () { .catch(error => error); expect(client).to.have.nested.property('s.authProviders'); - const provider = client.s.authProviders.getOrCreateProvider('MONGODB-AWS'); + const provider = client.s.authProviders.getOrCreateProvider('MONGODB-AWS', {}); expect(provider).to.be.instanceOf(MongoDBAWS); }); diff --git a/test/integration/client-side-encryption/client_side_encryption.prose.26.custom_aws_credential_providers.test.ts b/test/integration/client-side-encryption/client_side_encryption.prose.26.custom_aws_credential_providers.test.ts index 078705d7b4b..ed42a92e9c8 100644 --- a/test/integration/client-side-encryption/client_side_encryption.prose.26.custom_aws_credential_providers.test.ts +++ b/test/integration/client-side-encryption/client_side_encryption.prose.26.custom_aws_credential_providers.test.ts @@ -102,4 +102,49 @@ describe('26. Custom AWS Credential Providers', metadata, () => { }); } ); + + context( + 'ClientEncryption with credentialProviders and valid environment variables', + metadata, + function () { + let clientEncryption; + let providerCount = 0; + let previousAccessKey; + let previousSecretKey; + + beforeEach(function () { + previousAccessKey = process.env.AWS_ACCESS_KEY_ID; + previousSecretKey = process.env.AWS_SECRET_ACCESS_KEY; + process.env.AWS_ACCESS_KEY_ID = process.env.FLE_AWS_KEY; + process.env.AWS_SECRET_ACCESS_KEY = process.env.FLE_AWS_SECRET; + + const options = { + keyVaultNamespace: 'keyvault.datakeys', + kmsProviders: { aws: {} }, + credentialProviders: { + aws: async () => { + providerCount++; + return { + accessKeyId: process.env.FLE_AWS_KEY, + secretAccessKey: process.env.FLE_AWS_SECRET + }; + } + }, + extraOptions: getEncryptExtraOptions() + }; + clientEncryption = new ClientEncryption(keyVaultClient, options); + }); + + afterEach(function () { + process.env.AWS_ACCESS_KEY_ID = previousAccessKey; + process.env.AWS_SECRET_ACCESS_KEY = previousSecretKey; + }); + + it('is successful', metadata, async function () { + const dk = await clientEncryption.createDataKey('aws', { masterKey }); + expect(dk).to.be.instanceOf(Binary); + expect(providerCount).to.be.greaterThan(0); + }); + } + ); }); From 31e7f125d5d791ba60603ac101d3e1f5462ea931 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Mon, 22 Sep 2025 14:27:41 +0200 Subject: [PATCH 2/6] test: fix --- test/integration/auth/mongodb_aws.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/integration/auth/mongodb_aws.test.ts b/test/integration/auth/mongodb_aws.test.ts index d6efe7bcda5..42adc323d08 100644 --- a/test/integration/auth/mongodb_aws.test.ts +++ b/test/integration/auth/mongodb_aws.test.ts @@ -211,8 +211,7 @@ describe('MONGODB-AWS', function () { this.skipReason = 'Test only runs when credentials are present in the URI'; return this.skip(); } - // @ts-expect-error We intentionally access a protected variable. - const credentialProvider = AWSTemporaryCredentialProvider.awsSDK; + const credentialProvider = AWSSDKCredentialProvider.awsSDK; provider = async () => { providerCount++; return await credentialProvider.fromNodeProviderChain().apply(); From f169ec6c7ad89505561ac88016e9c2cb32cbb874 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Mon, 22 Sep 2025 15:02:07 +0200 Subject: [PATCH 3/6] test: fix --- test/integration/auth/mongodb_aws.test.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/integration/auth/mongodb_aws.test.ts b/test/integration/auth/mongodb_aws.test.ts index 42adc323d08..9c2ac245c97 100644 --- a/test/integration/auth/mongodb_aws.test.ts +++ b/test/integration/auth/mongodb_aws.test.ts @@ -247,8 +247,7 @@ describe('MONGODB-AWS', function () { this.skipReason = 'Test only runs when credentials are present in the environment'; return this.skip(); } - // @ts-expect-error We intentionally access a protected variable. - const credentialProvider = AWSTemporaryCredentialProvider.awsSDK; + const credentialProvider = AWSSDKCredentialProvider.awsSDK; provider = async () => { providerCount++; return await credentialProvider.fromNodeProviderChain().apply(); From e81226723933bea26f6f441753f4885aefc8f146 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Mon, 22 Sep 2025 16:00:14 +0200 Subject: [PATCH 4/6] fix: dont put env vars in credentials --- src/cmap/auth/mongo_credentials.ts | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/cmap/auth/mongo_credentials.ts b/src/cmap/auth/mongo_credentials.ts index 259147d5b07..9abe1ebbc5b 100644 --- a/src/cmap/auth/mongo_credentials.ts +++ b/src/cmap/auth/mongo_credentials.ts @@ -134,26 +134,6 @@ export class MongoCredentials { this.mechanism = options.mechanism || AuthMechanism.MONGODB_DEFAULT; this.mechanismProperties = options.mechanismProperties || {}; - if (this.mechanism.match(/MONGODB-AWS/i)) { - if (!this.username && process.env.AWS_ACCESS_KEY_ID) { - this.username = process.env.AWS_ACCESS_KEY_ID; - } - - if (!this.password && process.env.AWS_SECRET_ACCESS_KEY) { - this.password = process.env.AWS_SECRET_ACCESS_KEY; - } - - if ( - this.mechanismProperties.AWS_SESSION_TOKEN == null && - process.env.AWS_SESSION_TOKEN != null - ) { - this.mechanismProperties = { - ...this.mechanismProperties, - AWS_SESSION_TOKEN: process.env.AWS_SESSION_TOKEN - }; - } - } - if (this.mechanism === AuthMechanism.MONGODB_OIDC && !this.mechanismProperties.ALLOWED_HOSTS) { this.mechanismProperties = { ...this.mechanismProperties, From 1d0797ada40cbf07bf60d43058ee796cc5698a6f Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Wed, 24 Sep 2025 16:29:57 +0200 Subject: [PATCH 5/6] chore: comments --- test/integration/auth/mongodb_aws.test.ts | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/test/integration/auth/mongodb_aws.test.ts b/test/integration/auth/mongodb_aws.test.ts index 9c2ac245c97..8190acdd574 100644 --- a/test/integration/auth/mongodb_aws.test.ts +++ b/test/integration/auth/mongodb_aws.test.ts @@ -164,6 +164,15 @@ describe('MONGODB-AWS', function () { }); context('when using a custom credential provider', function () { + // NOTE: Logic for scenarios 1-6 is handled via the evergreen variant configs. + // Scenarios 1-6 from the previous section with a user provided AWS_CREDENTIAL_PROVIDER auth mechanism + // property. This credentials MAY be obtained from the default credential provider from the AWS SDK. + // If the default provider does not cover all scenarios above, those not covered MAY be skipped. + // In these tests the driver MUST also assert that the user provided credential provider was called + // in each test. This may be via a custom function or object that wraps the calls to the custom provider + // and asserts that it was called at least once. For test scenarios where the drivers tools scripts put + // the credentials in the MONGODB_URI, drivers MAY extract the credentials from the URI and return the AWS + // credentials directly from the custom provider instead of using the AWS SDK default provider. context('1. Custom Credential Provider Authenticates', function () { let providerCount = 0; @@ -201,12 +210,16 @@ describe('MONGODB-AWS', function () { }); context('2. Custom Credential Provider Authentication Precedence', function () { + // Create a MongoClient configured with AWS auth and credentials in the URI. + // Example: mongodb://:@localhost:27017/?authMechanism=MONGODB-AWS + // Configure a custom credential provider to pass valid AWS credentials. The provider must + // track if it was called. + // Expect authentication to succeed and the custom credential provider was not called. context('Case 1: Credentials in URI Take Precedence', function () { let providerCount = 0; let provider; beforeEach(function () { - console.log(client?.options); if (!client?.options.credentials.username) { this.skipReason = 'Test only runs when credentials are present in the URI'; return this.skip(); @@ -219,7 +232,6 @@ describe('MONGODB-AWS', function () { }); it('authenticates with a user provided credentials provider', async function () { - console.log(process.env); client = this.configuration.newClient(process.env.MONGODB_URI, { authMechanismProperties: { AWS_CREDENTIAL_PROVIDER: provider @@ -238,6 +250,11 @@ describe('MONGODB-AWS', function () { }); }); + // Run this test in an environment with AWS credentials configured as environment variables + // (e.g. AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, and AWS_SESSION_TOKEN) + // Create a MongoClient configured to use AWS auth. Example: mongodb://localhost:27017/?authMechanism=MONGODB-AWS. + // Configure a custom credential provider to pass valid AWS credentials. The provider must track if it was called. + // Expect authentication to succeed and the custom credential provider was called. context('Case 2: Custom Provider Takes Precedence Over Environment Variables', function () { let providerCount = 0; let provider; From 62930f8b8d82fc14f03316e805d164c7cfb03536 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Wed, 24 Sep 2025 16:34:21 +0200 Subject: [PATCH 6/6] chore: comments --- ...rose.26.custom_aws_credential_providers.test.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/test/integration/client-side-encryption/client_side_encryption.prose.26.custom_aws_credential_providers.test.ts b/test/integration/client-side-encryption/client_side_encryption.prose.26.custom_aws_credential_providers.test.ts index ed42a92e9c8..4790ddf808b 100644 --- a/test/integration/client-side-encryption/client_side_encryption.prose.26.custom_aws_credential_providers.test.ts +++ b/test/integration/client-side-encryption/client_side_encryption.prose.26.custom_aws_credential_providers.test.ts @@ -103,8 +103,20 @@ describe('26. Custom AWS Credential Providers', metadata, () => { } ); + // Ensure a valid AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY are present in the environment. + // Create a MongoClient named setupClient. + // Create a ClientEncryption object with the following options: + // class ClientEncryptionOpts { + // keyVaultClient: , + // keyVaultNamespace: "keyvault.datakeys", + // kmsProviders: { "aws": {} }, + // credentialProviders: { "aws": } + // } + // Use the client encryption to create a datakey using the "aws" KMS provider. This should successfully load + // and use the AWS credentials that were provided by the secrets manager for the remote provider. Assert the + // datakey was created and that the custom credential provider was called at least once. context( - 'ClientEncryption with credentialProviders and valid environment variables', + 'Case 4: ClientEncryption with credentialProviders and valid environment variables', metadata, function () { let clientEncryption;