Skip to content

Commit fb38a56

Browse files
syz99nbbeekendariakp
authored
fix(NODE-3795): unexpected No auth provider for DEFAULT defined error (#3092)
* update mock authSource * format * add test case for non-srv URI * fix: Add test and logic to ignore credentials if authSource is the only auth option * chore: add reasoning for test structure * fix: update assertion, and test name * test: title phrasing Co-authored-by: Daria Pardue <[email protected]> * fix: failing test * fix: test name Co-authored-by: Neal Beeken <[email protected]> Co-authored-by: Daria Pardue <[email protected]>
1 parent 2229d1a commit fb38a56

File tree

2 files changed

+64
-2
lines changed

2 files changed

+64
-2
lines changed

src/connection_string.ts

+10
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,16 @@ export function parseOptions(
394394
}
395395

396396
mongoOptions.credentials.validate();
397+
398+
// Check if the only auth related option provided was authSource, if so we can remove credentials
399+
if (
400+
mongoOptions.credentials.password === '' &&
401+
mongoOptions.credentials.username === '' &&
402+
mongoOptions.credentials.mechanism === AuthMechanism.MONGODB_DEFAULT &&
403+
Object.keys(mongoOptions.credentials.mechanismProperties).length === 0
404+
) {
405+
delete mongoOptions.credentials;
406+
}
397407
}
398408

399409
if (!mongoOptions.dbName) {

test/unit/connection_string.test.ts

+54-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@ import { promisify } from 'util';
66
import { MongoCredentials } from '../../src/cmap/auth/mongo_credentials';
77
import { AUTH_MECHS_AUTH_SRC_EXTERNAL, AuthMechanism } from '../../src/cmap/auth/providers';
88
import { parseOptions, resolveSRVRecord } from '../../src/connection_string';
9-
import { MongoDriverError, MongoInvalidArgumentError, MongoParseError } from '../../src/error';
10-
import { MongoOptions } from '../../src/mongo_client';
9+
import {
10+
MongoAPIError,
11+
MongoDriverError,
12+
MongoInvalidArgumentError,
13+
MongoParseError
14+
} from '../../src/error';
15+
import { MongoClient, MongoOptions } from '../../src/mongo_client';
1116

1217
describe('Connection String', function () {
1318
it('should not support auth passed with user', function () {
@@ -89,6 +94,37 @@ describe('Connection String', function () {
8994
expect(options.credentials.source).to.equal('$external');
9095
});
9196

97+
it('should omit credentials option when the only authSource is provided', function () {
98+
let options = parseOptions(`mongodb://a/?authSource=someDb`);
99+
expect(options).to.not.have.property('credentials');
100+
options = parseOptions(`mongodb+srv://a/?authSource=someDb`);
101+
expect(options).to.not.have.property('credentials');
102+
});
103+
104+
it('should omit credentials and not throw a MongoAPIError if the only auth related option is authSource', async () => {
105+
// The error we're looking to **not** see is
106+
// `new MongoInvalidArgumentError('No AuthProvider for ${credentials.mechanism} defined.')`
107+
// in `prepareHandshakeDocument` and/or `performInitialHandshake`.
108+
// Neither function is exported currently but if I did export them because of the inlined callbacks
109+
// I think I would need to mock quite a bit of internals to get down to that layer.
110+
// My thinking is I can lean on server selection failing for th unit tests to assert we at least don't get an error related to auth.
111+
const client = new MongoClient('mongodb://localhost:123/?authSource=someDb', {
112+
serverSelectionTimeoutMS: 500
113+
});
114+
115+
let thrownError: Error;
116+
try {
117+
// relies on us not running a mongod on port 123, fairly likely assumption
118+
await client.connect();
119+
} catch (error) {
120+
thrownError = error;
121+
}
122+
123+
// We should fail to connect, not fail to find an auth provider thus we should not find a MongoAPIError
124+
expect(thrownError).to.not.be.instanceOf(MongoAPIError);
125+
expect(client.options).to.not.have.a.property('credentials');
126+
});
127+
92128
it('should parse a numeric authSource with variable width', function () {
93129
const options = parseOptions('mongodb://test@localhost/?authSource=0001');
94130
expect(options.credentials.source).to.equal('0001');
@@ -334,5 +370,21 @@ describe('Connection String', function () {
334370
expect(options).property('credentials').to.equal(credentials);
335371
expect(options).to.have.nested.property('credentials.source', 'admin');
336372
});
373+
374+
it('should retain specified authSource with no provided credentials', async function () {
375+
makeStub('authSource=thisShouldBeAuthSource');
376+
const credentials = {};
377+
const options = {
378+
credentials,
379+
srvHost: 'test.mock.test.build.10gen.cc',
380+
srvServiceName: 'mongodb',
381+
userSpecifiedAuthSource: false
382+
} as MongoOptions;
383+
384+
await resolveSRVRecordAsync(options as any);
385+
expect(options).to.have.nested.property('credentials.username', '');
386+
expect(options).to.have.nested.property('credentials.mechanism', 'DEFAULT');
387+
expect(options).to.have.nested.property('credentials.source', 'thisShouldBeAuthSource');
388+
});
337389
});
338390
});

0 commit comments

Comments
 (0)