Skip to content

Commit 09f5ca6

Browse files
authored
fix(oidc, atlas-service): use system ca certs in atlas reqs and OIDC COMPASS-7950 (#5925)
1 parent 866e1a3 commit 09f5ca6

File tree

4 files changed

+54
-13
lines changed

4 files changed

+54
-13
lines changed

package-lock.json

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/atlas-service/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
"react": "^17.0.2",
8989
"react-redux": "^8.1.3",
9090
"redux": "^4.2.1",
91-
"redux-thunk": "^2.4.2"
91+
"redux-thunk": "^2.4.2",
92+
"system-ca": "^1.0.2"
9293
}
9394
}

packages/atlas-service/src/main.spec.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ describe('CompassAuthServiceMain', function () {
9494

9595
CompassAuthService['config'] = defaultConfig;
9696

97-
CompassAuthService['setupPlugin']();
97+
await CompassAuthService['setupPlugin']();
9898
CompassAuthService['attachOidcPluginLoggerEvents']();
9999

100100
preferences = await createSandboxFromDefaultPreferences();
@@ -298,6 +298,24 @@ describe('CompassAuthServiceMain', function () {
298298
await CompassAuthService.init(preferences);
299299
expect(setupPluginSpy).to.have.been.calledOnce;
300300
});
301+
302+
it('should pass the system ca to the plugin as a custom http option', async function () {
303+
const createOIDCPluginSpy = sandbox.spy(
304+
CompassAuthService as any,
305+
'createMongoDBOIDCPlugin'
306+
);
307+
await CompassAuthService.init(preferences);
308+
expect(createOIDCPluginSpy).to.have.been.calledOnce;
309+
try {
310+
expect(
311+
createOIDCPluginSpy.firstCall.args[0].customHttpOptions.ca
312+
).to.include('-----BEGIN CERTIFICATE-----');
313+
} catch (e) {
314+
throw new Error(
315+
'Expected ca to be included in the customHttpOptions, but it was not.'
316+
);
317+
}
318+
});
301319
});
302320

303321
describe('with networkTraffic turned off', function () {

packages/atlas-service/src/main.ts

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ import {
1111
hookLoggerToMongoLogWriter as oidcPluginHookLoggerToMongoLogWriter,
1212
} from '@mongodb-js/oidc-plugin';
1313
import { oidcServerRequestHandler } from '@mongodb-js/devtools-connect';
14-
// TODO(https://github.com/node-fetch/node-fetch/issues/1652): Remove this when
15-
// node-fetch types match the built in AbortSignal from node.
16-
import type { AbortSignal as NodeFetchAbortSignal } from 'node-fetch/externals';
14+
import { systemCertsAsync } from 'system-ca';
15+
import type { Options as SystemCAOptions } from 'system-ca';
1716
import type { RequestInfo, RequestInit, Response } from 'node-fetch';
17+
import https from 'https';
1818
import nodeFetch from 'node-fetch';
1919
import type { IntrospectInfo, AtlasUserInfo, AtlasServiceConfig } from './util';
2020
import { throwIfAborted } from '@mongodb-js/compass-utils';
@@ -37,6 +37,15 @@ const redirectRequestHandler = oidcServerRequestHandler.bind(null, {
3737
productDocsLink: 'https://www.mongodb.com/docs/compass',
3838
});
3939

40+
async function getSystemCA() {
41+
// It is possible for OIDC login flow to fail if system CA certs are different from
42+
// the ones packaged with the application. To avoid this, we include the system CA
43+
// certs in the OIDC plugin options. See COMPASS-7950 for more details.
44+
const systemCAOpts: SystemCAOptions = { includeNodeCertificates: true };
45+
const ca = await systemCertsAsync(systemCAOpts);
46+
return ca.join('\n');
47+
}
48+
4049
const TOKEN_TYPE_TO_HINT = {
4150
accessToken: 'access_token',
4251
refreshToken: 'refresh_token',
@@ -63,7 +72,7 @@ export class CompassAuthService {
6372
): Promise<Response> => {
6473
await this.initPromise;
6574
this.throwIfNetworkTrafficDisabled();
66-
throwIfAborted(init.signal as AbortSignal);
75+
throwIfAborted(init.signal ?? undefined);
6776
log.info(
6877
mongoLogId(1_001_000_299),
6978
'AtlasService',
@@ -72,6 +81,14 @@ export class CompassAuthService {
7281
);
7382
try {
7483
const res = await nodeFetch(url, {
84+
// Tests use 'http'.
85+
...(url.toString().includes('https')
86+
? {
87+
agent: new https.Agent({
88+
ca: await getSystemCA(),
89+
}),
90+
}
91+
: {}),
7592
...init,
7693
headers: {
7794
...init.headers,
@@ -129,7 +146,7 @@ export class CompassAuthService {
129146

130147
private static createMongoDBOIDCPlugin = createMongoDBOIDCPlugin;
131148

132-
private static setupPlugin(serializedState?: string) {
149+
private static async setupPlugin(serializedState?: string) {
133150
this.plugin = this.createMongoDBOIDCPlugin({
134151
redirectServerRequestHandler: (data) => {
135152
if (data.result === 'redirecting') {
@@ -150,6 +167,9 @@ export class CompassAuthService {
150167
allowedFlows: this.getAllowedAuthFlows.bind(this),
151168
logger: this.oidcPluginLogger,
152169
serializedState,
170+
customHttpOptions: {
171+
ca: await getSystemCA(),
172+
},
153173
});
154174
oidcPluginHookLoggerToMongoLogWriter(
155175
this.oidcPluginLogger,
@@ -180,7 +200,7 @@ export class CompassAuthService {
180200
{ config: this.config }
181201
);
182202
const serializedState = await this.secretStore.getState();
183-
this.setupPlugin(serializedState);
203+
await this.setupPlugin(serializedState);
184204
})());
185205
}
186206

@@ -301,7 +321,7 @@ export class CompassAuthService {
301321
this.attachOidcPluginLoggerEvents();
302322
// Destroy old plugin and setup new one
303323
await this.plugin?.destroy();
304-
this.setupPlugin();
324+
await this.setupPlugin();
305325
// Revoke tokens. Revoking refresh token will also revoke associated access
306326
// tokens
307327
// https://developer.okta.com/docs/guides/revoke-tokens/main/#revoke-an-access-token-or-a-refresh-token
@@ -362,7 +382,7 @@ export class CompassAuthService {
362382
Authorization: `Bearer ${token ?? ''}`,
363383
Accept: 'application/json',
364384
},
365-
signal: signal as NodeFetchAbortSignal | undefined,
385+
signal: signal,
366386
}
367387
);
368388

@@ -403,7 +423,7 @@ export class CompassAuthService {
403423
Accept: 'application/json',
404424
'Content-Type': 'application/x-www-form-urlencoded',
405425
},
406-
signal: signal as NodeFetchAbortSignal | undefined,
426+
signal: signal,
407427
});
408428

409429
await throwIfNotOk(res);
@@ -438,7 +458,7 @@ export class CompassAuthService {
438458
Accept: 'application/json',
439459
'Content-Type': 'application/x-www-form-urlencoded',
440460
},
441-
signal: signal as NodeFetchAbortSignal | undefined,
461+
signal: signal,
442462
});
443463

444464
await throwIfNotOk(res);

0 commit comments

Comments
 (0)