From 20a4fec5fa013c41f525f8c8c465b8f0db380bb2 Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Wed, 8 Feb 2023 23:10:44 +0100 Subject: [PATCH] feat(NODE-5034): support OIDC auth options (#3557) --- src/cmap/auth/mongo_credentials.ts | 59 ++++++- src/cmap/auth/mongodb_oidc.ts | 39 +++++ src/cmap/auth/providers.ts | 5 +- src/connection_string.ts | 46 +++--- src/index.ts | 6 + .../auth/{ => legacy}/connection-string.json | 152 +++++++++++++++++- .../auth/{ => legacy}/connection-string.yml | 118 ++++++++++++++ test/tools/uri_spec_runner.ts | 46 +++++- test/unit/assorted/auth.spec.test.ts | 4 +- test/unit/connection_string.test.ts | 12 +- 10 files changed, 456 insertions(+), 31 deletions(-) create mode 100644 src/cmap/auth/mongodb_oidc.ts rename test/spec/auth/{ => legacy}/connection-string.json (76%) rename test/spec/auth/{ => legacy}/connection-string.yml (77%) diff --git a/src/cmap/auth/mongo_credentials.ts b/src/cmap/auth/mongo_credentials.ts index fa461006f9..384c83315f 100644 --- a/src/cmap/auth/mongo_credentials.ts +++ b/src/cmap/auth/mongo_credentials.ts @@ -1,7 +1,12 @@ // Resolves the default auth mechanism according to import type { Document } from '../../bson'; -import { MongoAPIError, MongoMissingCredentialsError } from '../../error'; +import { + MongoAPIError, + MongoInvalidArgumentError, + MongoMissingCredentialsError +} from '../../error'; import { GSSAPICanonicalizationValue } from './gssapi'; +import type { OIDCRefreshFunction, OIDCRequestFunction } from './mongodb_oidc'; import { AUTH_MECHS_AUTH_SRC_EXTERNAL, AuthMechanism } from './providers'; // https://github.com/mongodb/specifications/blob/master/source/auth/auth.rst @@ -25,13 +30,25 @@ function getDefaultAuthMechanism(hello?: Document): AuthMechanism { return AuthMechanism.MONGODB_CR; } -/** @public */ +/** + * TODO: NODE-5035: Make OIDC properties public. + * + * @public + * */ export interface AuthMechanismProperties extends Document { SERVICE_HOST?: string; SERVICE_NAME?: string; SERVICE_REALM?: string; CANONICALIZE_HOST_NAME?: GSSAPICanonicalizationValue; AWS_SESSION_TOKEN?: string; + /** @internal Name for the OIDC device workflow */ + DEVICE_NAME?: 'aws' | 'azure' | 'gcp'; + /** @internal Similar to a username, is require by OIDC when more than one IDP is configured. */ + PRINCIPAL_NAME?: string; + /** @internal User provided callback to get OIDC auth credentials */ + REQUEST_TOKEN_CALLBACK?: OIDCRequestFunction; + /** @internal User provided callback to refresh OIDC auth credentials */ + REFRESH_TOKEN_CALLBACK?: OIDCRefreshFunction; } /** @public */ @@ -137,6 +154,44 @@ export class MongoCredentials { throw new MongoMissingCredentialsError(`Username required for mechanism '${this.mechanism}'`); } + if (this.mechanism === AuthMechanism.MONGODB_OIDC) { + if (this.username) { + throw new MongoInvalidArgumentError( + `Username not permitted for mechanism '${this.mechanism}'. Use PRINCIPAL_NAME instead.` + ); + } + + if (this.mechanismProperties.PRINCIPAL_NAME && this.mechanismProperties.DEVICE_NAME) { + throw new MongoInvalidArgumentError( + `PRINCIPAL_NAME and DEVICE_NAME may not be used together for mechanism '${this.mechanism}'.` + ); + } + + if (this.mechanismProperties.DEVICE_NAME && this.mechanismProperties.DEVICE_NAME !== 'aws') { + throw new MongoInvalidArgumentError( + `Currently only a DEVICE_NAME of 'aws' is supported for mechanism '${this.mechanism}'.` + ); + } + + if ( + this.mechanismProperties.REFRESH_TOKEN_CALLBACK && + !this.mechanismProperties.REQUEST_TOKEN_CALLBACK + ) { + throw new MongoInvalidArgumentError( + `A REQUEST_TOKEN_CALLBACK must be provided when using a REFRESH_TOKEN_CALLBACK for mechanism '${this.mechanism}'` + ); + } + + if ( + !this.mechanismProperties.DEVICE_NAME && + !this.mechanismProperties.REQUEST_TOKEN_CALLBACK + ) { + throw new MongoInvalidArgumentError( + `Either a DEVICE_NAME or a REQUEST_TOKEN_CALLBACK must be specified for mechanism '${this.mechanism}'.` + ); + } + } + if (AUTH_MECHS_AUTH_SRC_EXTERNAL.has(this.mechanism)) { if (this.source != null && this.source !== '$external') { // TODO(NODE-3485): Replace this with a MongoAuthValidationError diff --git a/src/cmap/auth/mongodb_oidc.ts b/src/cmap/auth/mongodb_oidc.ts new file mode 100644 index 0000000000..2763ef54fa --- /dev/null +++ b/src/cmap/auth/mongodb_oidc.ts @@ -0,0 +1,39 @@ +/** + * TODO: NODE-5035: Make API public + * + * @internal */ +export interface OIDCMechanismServerStep1 { + authorizeEndpoint?: string; + tokenEndpoint?: string; + deviceAuthorizeEndpoint?: string; + clientId: string; + clientSecret?: string; + requestScopes?: string[]; +} + +/** + * TODO: NODE-5035: Make API public + * + * @internal */ +export interface OIDCRequestTokenResult { + accessToken: string; + expiresInSeconds?: number; + refreshToken?: string; +} + +/** + * TODO: NODE-5035: Make API public + * + * @internal */ +export type OIDCRequestFunction = ( + idl: OIDCMechanismServerStep1 +) => Promise; + +/** + * TODO: NODE-5035: Make API public + * + * @internal */ +export type OIDCRefreshFunction = ( + idl: OIDCMechanismServerStep1, + result: OIDCRequestTokenResult +) => Promise; diff --git a/src/cmap/auth/providers.ts b/src/cmap/auth/providers.ts index 9c7b1f4b82..dc286a4b8e 100644 --- a/src/cmap/auth/providers.ts +++ b/src/cmap/auth/providers.ts @@ -7,7 +7,9 @@ export const AuthMechanism = Object.freeze({ MONGODB_PLAIN: 'PLAIN', MONGODB_SCRAM_SHA1: 'SCRAM-SHA-1', MONGODB_SCRAM_SHA256: 'SCRAM-SHA-256', - MONGODB_X509: 'MONGODB-X509' + MONGODB_X509: 'MONGODB-X509', + /** @internal TODO: NODE-5035: Make mechanism public. */ + MONGODB_OIDC: 'MONGODB-OIDC' } as const); /** @public */ @@ -17,5 +19,6 @@ export type AuthMechanism = typeof AuthMechanism[keyof typeof AuthMechanism]; export const AUTH_MECHS_AUTH_SRC_EXTERNAL = new Set([ AuthMechanism.MONGODB_GSSAPI, AuthMechanism.MONGODB_AWS, + AuthMechanism.MONGODB_OIDC, AuthMechanism.MONGODB_X509 ]); diff --git a/src/connection_string.ts b/src/connection_string.ts index d18bbf0eb6..75d6298fff 100644 --- a/src/connection_string.ts +++ b/src/connection_string.ts @@ -386,6 +386,7 @@ export function parseOptions( const isGssapi = mongoOptions.credentials.mechanism === AuthMechanism.MONGODB_GSSAPI; const isX509 = mongoOptions.credentials.mechanism === AuthMechanism.MONGODB_X509; const isAws = mongoOptions.credentials.mechanism === AuthMechanism.MONGODB_AWS; + const isOidc = mongoOptions.credentials.mechanism === AuthMechanism.MONGODB_OIDC; if ( (isGssapi || isX509) && allOptions.has('authSource') && @@ -397,7 +398,11 @@ export function parseOptions( ); } - if (!(isGssapi || isX509 || isAws) && mongoOptions.dbName && !allOptions.has('authSource')) { + if ( + !(isGssapi || isX509 || isAws || isOidc) && + mongoOptions.dbName && + !allOptions.has('authSource') + ) { // inherit the dbName unless GSSAPI or X509, then silently ignore dbName // and there was no specific authSource given mongoOptions.credentials = MongoCredentials.merge(mongoOptions.credentials, { @@ -678,26 +683,31 @@ export const OPTIONS = { }, authMechanismProperties: { target: 'credentials', - transform({ options, values: [optionValue] }): MongoCredentials { - if (typeof optionValue === 'string') { - const mechanismProperties = Object.create(null); - - for (const [key, value] of entriesFromString(optionValue)) { - try { - mechanismProperties[key] = getBoolean(key, value); - } catch { - mechanismProperties[key] = value; + transform({ options, values }): MongoCredentials { + // We can have a combination of options passed in the URI and options passed + // as an object to the MongoClient. So we must transform the string options + // as well as merge them together with a potentially provided object. + let mechanismProperties = Object.create(null); + + for (const optionValue of values) { + if (typeof optionValue === 'string') { + for (const [key, value] of entriesFromString(optionValue)) { + try { + mechanismProperties[key] = getBoolean(key, value); + } catch { + mechanismProperties[key] = value; + } } + } else { + if (!isRecord(optionValue)) { + throw new MongoParseError('AuthMechanismProperties must be an object'); + } + mechanismProperties = { ...optionValue }; } - - return MongoCredentials.merge(options.credentials, { - mechanismProperties - }); - } - if (!isRecord(optionValue)) { - throw new MongoParseError('AuthMechanismProperties must be an object'); } - return MongoCredentials.merge(options.credentials, { mechanismProperties: optionValue }); + return MongoCredentials.merge(options.credentials, { + mechanismProperties + }); } }, authSource: { diff --git a/src/index.ts b/src/index.ts index ad382f49b2..22ab7f4b99 100644 --- a/src/index.ts +++ b/src/index.ts @@ -202,6 +202,12 @@ export type { MongoCredentials, MongoCredentialsOptions } from './cmap/auth/mongo_credentials'; +export type { + OIDCMechanismServerStep1, + OIDCRefreshFunction, + OIDCRequestFunction, + OIDCRequestTokenResult +} from './cmap/auth/mongodb_oidc'; export type { BinMsg, MessageHeader, diff --git a/test/spec/auth/connection-string.json b/test/spec/auth/legacy/connection-string.json similarity index 76% rename from test/spec/auth/connection-string.json rename to test/spec/auth/legacy/connection-string.json index 300268a43d..f958ae60ae 100644 --- a/test/spec/auth/connection-string.json +++ b/test/spec/auth/legacy/connection-string.json @@ -480,6 +480,156 @@ "AWS_SESSION_TOKEN": "token!@#$%^&*()_+" } } + }, + { + "description": "should recognise the mechanism and request callback (MONGODB-OIDC)", + "uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC", + "callback": [ + "oidcRequest" + ], + "valid": true, + "credential": { + "username": null, + "password": null, + "source": "$external", + "mechanism": "MONGODB-OIDC", + "mechanism_properties": { + "REQUEST_TOKEN_CALLBACK": true + } + } + }, + { + "description": "should recognise the mechanism when auth source is explicitly specified and with request callback (MONGODB-OIDC)", + "uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC&authSource=$external", + "callback": [ + "oidcRequest" + ], + "valid": true, + "credential": { + "username": null, + "password": null, + "source": "$external", + "mechanism": "MONGODB-OIDC", + "mechanism_properties": { + "REQUEST_TOKEN_CALLBACK": true + } + } + }, + { + "description": "should recognise the mechanism with request and refresh callback (MONGODB-OIDC)", + "uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC&authSource=$external", + "callback": [ + "oidcRequest", + "oidcRefresh" + ], + "valid": true, + "credential": { + "username": null, + "password": null, + "source": "$external", + "mechanism": "MONGODB-OIDC", + "mechanism_properties": { + "REQUEST_TOKEN_CALLBACK": true, + "REFRESH_TOKEN_CALLBACK": true + } + } + }, + { + "description": "should recognise the mechanism and principalName with request callback (MONGODB-OIDC)", + "uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=PRINCIPAL_NAME:principalName", + "callback": [ + "oidcRequest" + ], + "valid": true, + "credential": { + "username": null, + "password": null, + "source": "$external", + "mechanism": "MONGODB-OIDC", + "mechanism_properties": { + "REQUEST_TOKEN_CALLBACK": true, + "PRINCIPAL_NAME": "principalName" + } + } + }, + { + "description": "should recognise the mechanism with aws device (MONGODB-OIDC)", + "uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=DEVICE_NAME:aws", + "valid": true, + "credential": { + "username": null, + "password": null, + "source": "$external", + "mechanism": "MONGODB-OIDC", + "mechanism_properties": { + "DEVICE_NAME": "aws" + } + } + }, + { + "description": "should recognise the mechanism when auth source is explicitly specified and with aws device (MONGODB-OIDC)", + "uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC&authSource=$external&authMechanismProperties=DEVICE_NAME:aws", + "valid": true, + "credential": { + "username": null, + "password": null, + "source": "$external", + "mechanism": "MONGODB-OIDC", + "mechanism_properties": { + "DEVICE_NAME": "aws" + } + } + }, + { + "description": "should throw an exception if username is specified (MONGODB-OIDC)", + "uri": "mongodb://user@localhost/?authMechanism=MONGODB-OIDC", + "callback": [ + "oidcRequest" + ], + "valid": false, + "credential": null + }, + { + "description": "should throw an exception if username and password are specified (MONGODB-OIDC)", + "uri": "mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC", + "callback": [ + "oidcRequest" + ], + "valid": false, + "credential": null + }, + { + "description": "should throw an exception if principalName and deviceName are specified (MONGODB-OIDC)", + "uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=PRINCIPAL_NAME:principalName,DEVICE_NAME:aws", + "valid": false, + "credential": null + }, + { + "description": "should throw an exception if specified deviceName is not supported (MONGODB-OIDC)", + "uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=DEVICE_NAME:unexisted", + "valid": false, + "credential": null + }, + { + "description": "should throw an exception if neither deviceName nor callbacks specified (MONGODB-OIDC)", + "uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC", + "valid": false, + "credential": null + }, + { + "description": "should throw an exception when only refresh callback is specified (MONGODB-OIDC)", + "uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC", + "callback": [ + "oidcRefresh" + ], + "valid": false, + "credential": null + }, + { + "description": "should throw an exception when unsupported auth property is specified (MONGODB-OIDC)", + "uri": "mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=UnsupportedProperty:unexisted", + "valid": false, + "credential": null } ] -} +} \ No newline at end of file diff --git a/test/spec/auth/connection-string.yml b/test/spec/auth/legacy/connection-string.yml similarity index 77% rename from test/spec/auth/connection-string.yml rename to test/spec/auth/legacy/connection-string.yml index 408c76e373..f595bc9494 100644 --- a/test/spec/auth/connection-string.yml +++ b/test/spec/auth/legacy/connection-string.yml @@ -393,3 +393,121 @@ tests: mechanism: "MONGODB-AWS" mechanism_properties: AWS_SESSION_TOKEN: "token!@#$%^&*()_+" + - + description: "should recognise the mechanism and request callback (MONGODB-OIDC)" + uri: "mongodb://localhost/?authMechanism=MONGODB-OIDC" + callback: + - "oidcRequest" + valid: true + credential: + username: null + password: null + source: "$external" + mechanism: "MONGODB-OIDC" + mechanism_properties: + REQUEST_TOKEN_CALLBACK: true + - + description: "should recognise the mechanism when auth source is explicitly specified and with request callback (MONGODB-OIDC)" + uri: "mongodb://localhost/?authMechanism=MONGODB-OIDC&authSource=$external" + callback: + - "oidcRequest" + valid: true + credential: + username: null + password: null + source: "$external" + mechanism: "MONGODB-OIDC" + mechanism_properties: + REQUEST_TOKEN_CALLBACK: true + - + description: "should recognise the mechanism with request and refresh callback (MONGODB-OIDC)" + uri: "mongodb://localhost/?authMechanism=MONGODB-OIDC&authSource=$external" + callback: + - "oidcRequest" + - "oidcRefresh" + valid: true + credential: + username: null + password: null + source: "$external" + mechanism: "MONGODB-OIDC" + mechanism_properties: + REQUEST_TOKEN_CALLBACK: true + REFRESH_TOKEN_CALLBACK: true + - + description: "should recognise the mechanism and principalName with request callback (MONGODB-OIDC)" + uri: "mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=PRINCIPAL_NAME:principalName" + callback: + - "oidcRequest" + valid: true + credential: + username: null + password: null + source: "$external" + mechanism: "MONGODB-OIDC" + mechanism_properties: + REQUEST_TOKEN_CALLBACK: true + PRINCIPAL_NAME: "principalName" + - + description: "should recognise the mechanism with aws device (MONGODB-OIDC)" + uri: "mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=DEVICE_NAME:aws" + valid: true + credential: + username: null + password: null + source: "$external" + mechanism: "MONGODB-OIDC" + mechanism_properties: + DEVICE_NAME: "aws" + - + description: "should recognise the mechanism when auth source is explicitly specified and with aws device (MONGODB-OIDC)" + uri: "mongodb://localhost/?authMechanism=MONGODB-OIDC&authSource=$external&authMechanismProperties=DEVICE_NAME:aws" + valid: true + credential: + username: null + password: null + source: "$external" + mechanism: "MONGODB-OIDC" + mechanism_properties: + DEVICE_NAME: "aws" + - + description: "should throw an exception if username is specified (MONGODB-OIDC)" + uri: "mongodb://user@localhost/?authMechanism=MONGODB-OIDC" + callback: + - "oidcRequest" + valid: false + credential: null + - + description: "should throw an exception if username and password are specified (MONGODB-OIDC)" + uri: "mongodb://user:pass@localhost/?authMechanism=MONGODB-OIDC" + callback: + - "oidcRequest" + valid: false + credential: null + - + description: "should throw an exception if principalName and deviceName are specified (MONGODB-OIDC)" + uri: "mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=PRINCIPAL_NAME:principalName,DEVICE_NAME:aws" + valid: false + credential: null + - + description: "should throw an exception if specified deviceName is not supported (MONGODB-OIDC)" + uri: "mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=DEVICE_NAME:unexisted" + valid: false + credential: null + - + description: "should throw an exception if neither deviceName nor callbacks specified (MONGODB-OIDC)" + uri: "mongodb://localhost/?authMechanism=MONGODB-OIDC" + valid: false + credential: null + - + description: "should throw an exception when only refresh callback is specified (MONGODB-OIDC)" + uri: "mongodb://localhost/?authMechanism=MONGODB-OIDC" + callback: + - "oidcRefresh" + valid: false + credential: null + - + description: "should throw an exception when unsupported auth property is specified (MONGODB-OIDC)" + uri: "mongodb://localhost/?authMechanism=MONGODB-OIDC&authMechanismProperties=UnsupportedProperty:unexisted" + valid: false + credential: null diff --git a/test/tools/uri_spec_runner.ts b/test/tools/uri_spec_runner.ts index 12bf1fd75e..492043aa11 100644 --- a/test/tools/uri_spec_runner.ts +++ b/test/tools/uri_spec_runner.ts @@ -48,6 +48,7 @@ export function executeUriValidationTest( shouldNotThrowOnWarn = false ): void { const knownTestKeys = [ + 'callback', 'description', 'uri', 'valid', @@ -84,7 +85,34 @@ export function executeUriValidationTest( return; } - const client = new MongoClient(test.uri); + // If a callback is specified in the spec test, we need to pass in references to those callbacks + // in the actual options provided to the MongoClient. This is because OIDC does not allow + // functions for callbacks in the URI itself but needs to validate they are passed. + const CALLBACKS = { + oidcRequest: async () => { + return { accessToken: '' }; + }, + oidcRefresh: async () => { + return { accessToken: '' }; + } + }; + + const CALLBACK_MAPPINGS = { + oidcRequest: 'REQUEST_TOKEN_CALLBACK', + oidcRefresh: 'REFRESH_TOKEN_CALLBACK' + }; + + const mongoClientOptions = {}; + + if (test.callback) { + const authMechanismProperties = Object.create(null); + for (const callback of test.callback) { + authMechanismProperties[CALLBACK_MAPPINGS[callback]] = CALLBACKS[callback]; + } + mongoClientOptions.authMechanismProperties = authMechanismProperties; + } + + const client = new MongoClient(test.uri, mongoClientOptions); const options = client.options; expect(options, errorMessage).to.be.an('object').that.is.not.empty; @@ -195,9 +223,19 @@ export function executeUriValidationTest( // TODO(NODE-3925): Ensure default SERVICE_NAME is set on the parsed mechanism properties continue; } - expect(options, `${errorMessage} credentials.mechanismProperties.${expectedMechProp}`) - .to.have.nested.property(`credentials.mechanismProperties.${expectedMechProp}`) - .equal(expectedMechValue); + if ( + expectedMechProp === 'REQUEST_TOKEN_CALLBACK' || + expectedMechProp === 'REFRESH_TOKEN_CALLBACK' + ) { + expect( + options, + `${errorMessage} credentials.mechanismProperties.${expectedMechProp}` + ).to.have.nested.property(`credentials.mechanismProperties.${expectedMechProp}`); + } else { + expect(options, `${errorMessage} credentials.mechanismProperties.${expectedMechProp}`) + .to.have.nested.property(`credentials.mechanismProperties.${expectedMechProp}`) + .equal(expectedMechValue); + } } break; diff --git a/test/unit/assorted/auth.spec.test.ts b/test/unit/assorted/auth.spec.test.ts index 560ec28462..c474fd8cf1 100644 --- a/test/unit/assorted/auth.spec.test.ts +++ b/test/unit/assorted/auth.spec.test.ts @@ -1,8 +1,8 @@ import { loadSpecTests } from '../../spec'; import { executeUriValidationTest } from '../../tools/uri_spec_runner'; -describe('Auth option spec tests', function () { - const suites = loadSpecTests('auth'); +describe('Auth option spec tests (legacy)', function () { + const suites = loadSpecTests('auth', 'legacy'); for (const suite of suites) { describe(suite.name, function () { diff --git a/test/unit/connection_string.test.ts b/test/unit/connection_string.test.ts index 4217de4c9a..6c389ed2e9 100644 --- a/test/unit/connection_string.test.ts +++ b/test/unit/connection_string.test.ts @@ -401,7 +401,7 @@ describe('Connection String', function () { it('should validate authMechanism', function () { expect(() => parseOptions('mongodb://localhost/?authMechanism=DOGS')).to.throw( MongoParseError, - 'authMechanism one of MONGODB-AWS,MONGODB-CR,DEFAULT,GSSAPI,PLAIN,SCRAM-SHA-1,SCRAM-SHA-256,MONGODB-X509, got DOGS' + 'authMechanism one of MONGODB-AWS,MONGODB-CR,DEFAULT,GSSAPI,PLAIN,SCRAM-SHA-1,SCRAM-SHA-256,MONGODB-X509,MONGODB-OIDC, got DOGS' ); }); @@ -452,13 +452,19 @@ describe('Connection String', function () { for (const mechanism of AUTH_MECHS_AUTH_SRC_EXTERNAL) { it(`should set authSource to $external for ${mechanism} external mechanism`, async function () { makeStub('authSource=thisShouldNotBeAuthSource'); + const mechanismProperties = {}; + if (mechanism === AuthMechanism.MONGODB_OIDC) { + mechanismProperties.DEVICE_NAME = 'aws'; + } + const credentials = new MongoCredentials({ source: '$external', mechanism, - username: 'username', + username: mechanism === AuthMechanism.MONGODB_OIDC ? undefined : 'username', password: mechanism === AuthMechanism.MONGODB_X509 ? undefined : 'password', - mechanismProperties: {} + mechanismProperties: mechanismProperties }); + credentials.validate(); const options = {