Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(NODE-6407): use conversationId returned from server in saslContinue #4368

Merged
merged 12 commits into from
Jan 31, 2025
2 changes: 1 addition & 1 deletion src/cmap/auth/mongodb_aws.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export class MongoDBAWS extends AuthProvider {

const saslContinue = {
saslContinue: 1,
conversationId: 1,
conversationId: saslStartResponse.conversationId,
payload: BSON.serialize(payload, bsonOptions)
};

Expand Down
76 changes: 76 additions & 0 deletions test/integration/auth/mongodb_aws.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,15 @@ import * as sinon from 'sinon';
import { refreshKMSCredentials } from '../../../src/client-side-encryption/providers';
import {
AWSTemporaryCredentialProvider,
type CommandOptions,
Connection,
type Document,
MongoAWSError,
type MongoClient,
MongoDBAWS,
type MongoDBNamespace,
type MongoDBResponseConstructor,
MongoError,
MongoMissingCredentialsError,
MongoServerError,
setDifference
Expand Down Expand Up @@ -61,6 +67,76 @@ describe('MONGODB-AWS', function () {
expect(result).to.be.a('number');
});

describe('ConversationId', function () {
let commandStub: sinon.SinonStub<
[
ns: MongoDBNamespace,
command: Document,
options?: CommandOptions,
responseType?: MongoDBResponseConstructor
],
Promise<any>
>;

let saslStartResult, saslContinue;

beforeEach(function () {
// spy on connection.command, filter for saslStart and saslContinue commands
commandStub = sinon.stub(Connection.prototype, 'command').callsFake(async function (
ns: MongoDBNamespace,
command: Document,
options: CommandOptions,
responseType?: MongoDBResponseConstructor
) {
if (command.saslContinue != null) {
saslContinue = { ...command };
}

const result = await commandStub.wrappedMethod.call(
this,
ns,
command,
options,
responseType
);

if (command.saslStart != null) {
// Modify the result of the saslStart to check if the saslContinue uses it
result.conversationId = 999;
saslStartResult = { ...result };
}

return result;
});
});

afterEach(function () {
commandStub.restore();
sinon.restore();
});

it('should use conversationId returned by saslStart in saslContinue', async function () {
client = this.configuration.newClient(process.env.MONGODB_URI); // use the URI built by the test environment

const err = await client
.db('aws')
.collection('aws_test')
.estimatedDocumentCount()
.catch(e => e);

// Expecting the saslContinue to fail since we changed the conversationId
expect(err).to.be.instanceof(MongoServerError);
expect(err.message).to.match(/Mismatched conversation id/);

expect(saslStartResult).to.not.be.undefined;
expect(saslContinue).to.not.be.undefined;

expect(saslStartResult).to.have.property('conversationId', 999);

expect(saslContinue).to.have.property('conversationId').equal(saslStartResult.conversationId);
});
});

it('should allow empty string in authMechanismProperties.AWS_SESSION_TOKEN to override AWS_SESSION_TOKEN environment variable', function () {
client = this.configuration.newClient(this.configuration.url(), {
authMechanismProperties: { AWS_SESSION_TOKEN: '' }
Expand Down
Loading