Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@mongodb-js/oidc-plugin

Package Overview
Dependencies
Maintainers
30
Versions
26
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@mongodb-js/oidc-plugin - npm Package Compare versions

Comparing version 0.1.0-alpha.0 to 0.1.0-alpha.1

dist/log-hook.d.ts

3

dist/api.d.ts
/// <reference types="node" />
/// <reference types="node" />
import type { IncomingMessage as HttpIncomingMessage, ServerResponse as HttpServerResponse } from 'http';
import { MongoDBOIDCPluginImpl } from './plugin';
import type { MongoDBOIDCLogEventsMap, OIDCAbortSignal, OIDCRefreshFunction, OIDCRequestFunction, TypedEventEmitter } from './types';

@@ -122,2 +123,4 @@ /** @public */

}
/** @internal */
export declare const publicPluginToInternalPluginMap_DoNotUseOutsideOfTests: WeakMap<MongoDBOIDCPlugin, MongoDBOIDCPluginImpl>;
/**

@@ -124,0 +127,0 @@ * Create a new OIDC plugin instance that can be passed to the Node.js MongoDB

8

dist/api.js
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.kDefaultOpenBrowserTimeout = exports.createMongoDBOIDCPlugin = void 0;
exports.kDefaultOpenBrowserTimeout = exports.createMongoDBOIDCPlugin = exports.publicPluginToInternalPluginMap_DoNotUseOutsideOfTests = void 0;
const plugin_1 = require("./plugin");
/** @internal */
exports.publicPluginToInternalPluginMap_DoNotUseOutsideOfTests = new WeakMap();
/**

@@ -18,6 +20,8 @@ * Create a new OIDC plugin instance that can be passed to the Node.js MongoDB

const plugin = new plugin_1.MongoDBOIDCPluginImpl({ ...options });
return {
const publicPlugin = {
mongoClientOptions: plugin.mongoClientOptions,
logger: plugin.logger,
};
exports.publicPluginToInternalPluginMap_DoNotUseOutsideOfTests.set(publicPlugin, plugin);
return publicPlugin;
}

@@ -24,0 +28,0 @@ exports.createMongoDBOIDCPlugin = createMongoDBOIDCPlugin;

export { createMongoDBOIDCPlugin } from './api';
export type { MongoDBOIDCPlugin, MongoDBOIDCPluginOptions, AuthFlowType, DeviceFlowInformation, OpenBrowserOptions, OpenBrowserReturnType, RedirectServerRequestHandler, RedirectServerRequestInfo, } from './api';
export type { TypedEventEmitter, OIDCRefreshFunction, OIDCRequestFunction, OIDCMechanismServerStep1, OIDCRequestTokenResult, OIDCAbortSignal, MongoDBOIDCError, MongoDBOIDCLogEventsMap, } from './types';
export { hookLoggerToMongoLogWriter, MongoLogWriter } from './log-hook';
//# sourceMappingURL=index.d.ts.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createMongoDBOIDCPlugin = void 0;
exports.hookLoggerToMongoLogWriter = exports.createMongoDBOIDCPlugin = void 0;
var api_1 = require("./api");
Object.defineProperty(exports, "createMongoDBOIDCPlugin", { enumerable: true, get: function () { return api_1.createMongoDBOIDCPlugin; } });
var log_hook_1 = require("./log-hook");
Object.defineProperty(exports, "hookLoggerToMongoLogWriter", { enumerable: true, get: function () { return log_hook_1.hookLoggerToMongoLogWriter; } });
//# sourceMappingURL=index.js.map
import type { MongoDBOIDCLogEventsMap, OIDCAbortSignal, OIDCMechanismServerStep1, OIDCRequestTokenResult, TypedEventEmitter } from './types';
import type { TokenSet } from 'openid-client';
import type { MongoDBOIDCPlugin, MongoDBOIDCPluginOptions } from './api';
/** @internal Exported for testing only */
export declare function automaticRefreshTimeoutMS(tokenSet: Pick<TokenSet, 'refresh_token' | 'expires_in'>): number | undefined;
/** @internal */

@@ -9,2 +12,3 @@ export declare class MongoDBOIDCPluginImpl implements MongoDBOIDCPlugin {

readonly mongoClientOptions: MongoDBOIDCPlugin['mongoClientOptions'];
private readonly timers;
constructor(options: Readonly<MongoDBOIDCPluginOptions>);

@@ -18,2 +22,3 @@ private isFlowAllowed;

private storeTokenSet;
private verifyValidUrl;
private authorizationCodeFlow;

@@ -20,0 +25,0 @@ private deviceAuthorizationFlow;

@@ -29,3 +29,3 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.MongoDBOIDCPluginImpl = void 0;
exports.MongoDBOIDCPluginImpl = exports.automaticRefreshTimeoutMS = void 0;
const types_1 = require("./types");

@@ -64,3 +64,28 @@ const util_1 = require("./util");

}
/** @internal Exported for testing only */
function automaticRefreshTimeoutMS(tokenSet) {
// If the tokens expire in more than 1 minute, automatically register
// a refresh handler. (They should not expire in less; however,
// if we didn't handle that case, we'd run the risk of refreshing very
// frequently.) Refresh the token 5 minutes before expiration or
// halfway between now and the expiration time, whichever comes later
// (expires in 1 hour -> refresh in 55 min, expires in 5 min -> refresh in 2.5 min).
if (tokenSet.refresh_token &&
tokenSet.expires_in &&
tokenSet.expires_in >= 60 /* 1 minute */) {
return (Math.max(tokenSet.expires_in - 300 /* 5 minutes */, tokenSet.expires_in / 2) * 1000);
}
}
exports.automaticRefreshTimeoutMS = automaticRefreshTimeoutMS;
const kEnableFallback = Symbol.for('@@mdb.oidcplugin.kEnableFallback');
function allowFallbackIfFailed(promise) {
return promise.catch((err) => {
// Tell the outer logic here to fallback to device auth flow if it is
// available if any of the steps above failed.
if (Object.isExtensible(err)) {
err[kEnableFallback] = true;
}
throw err;
});
}
/** @internal */

@@ -78,2 +103,3 @@ class MongoDBOIDCPluginImpl {

};
this.timers = { setTimeout, clearTimeout };
}

@@ -185,27 +211,55 @@ // Is this flow supported and allowed?

storeTokenSet(state, tokenSet, client) {
const timerDuration = automaticRefreshTimeoutMS(tokenSet);
let timer = timerDuration
? this.timers.setTimeout(() => void tryRefresh(), timerDuration).unref()
: undefined;
const tryRefresh = (0, util_1.withLock)(async () => {
if (timer) {
this.timers.clearTimeout(timer);
timer = undefined;
}
// Only refresh this token set if it is the one currently
// being used.
if (state.currentTokenSet?.set !== tokenSet)
return false;
try {
this.logger.emit('mongodb-oidc-plugin:refresh-started');
const refreshedTokens = await client.refresh(tokenSet);
// Check again to avoid race conditions.
if (state.currentTokenSet?.set === tokenSet) {
this.logger.emit('mongodb-oidc-plugin:refresh-succeeded');
this.storeTokenSet(state, refreshedTokens, client);
return true;
}
}
catch (err) {
this.logger.emit('mongodb-oidc-plugin:refresh-failed', {
error: (0, util_1.errorString)(err),
});
}
return false;
});
state.currentTokenSet = {
set: tokenSet,
tryRefresh: async () => {
// Only refresh this token set if it is the one currently
// being used.
if (state.currentTokenSet?.set !== tokenSet)
return false;
try {
const refreshedTokens = await client.refresh(tokenSet);
// Check again to avoid race conditions.
if (state.currentTokenSet?.set === tokenSet) {
this.storeTokenSet(state, refreshedTokens, client);
return true;
}
}
catch (err) {
this.logger.emit('mongodb-oidc-plugin:refresh-failed', {
error: (0, util_1.errorString)(err),
});
}
return false;
},
tryRefresh,
};
}
verifyValidUrl(serverOIDCMetadata, key) {
// Verify that `key` refers to a valid URL. This is currently
// *not* an error that we allow to fall back from.
const value = serverOIDCMetadata[key];
if (!value || typeof value !== 'string') {
throw new types_1.MongoDBOIDCError(`'${key}' is missing`);
}
else {
try {
new URL(value);
}
catch {
throw new types_1.MongoDBOIDCError(`'${key}' is invalid: ${value}}`);
}
}
}
async authorizationCodeFlow(state, signal) {
this.verifyValidUrl(state.serverOIDCMetadata, 'authorizationEndpoint');
const { scope, issuerParams, clientParams } = this.getInitialOIDCIssuerAndClientParams(state.serverOIDCMetadata);

@@ -224,6 +278,14 @@ const codeVerifier = openid_client_1.generators.codeVerifier();

let paramsUrl = '';
let enableFallback = true;
try {
await (0, util_1.withAbortCheck)(signal, async ({ signalCheck, signalPromise }) => {
await Promise.race([server.listen(), signalPromise]);
// We mark the operations that we want to allow to result in a fallback
// to potentially less secure flows explicitly.
// Specifically, we only do so if we cannot open a local HTTP server
// or a local browser. Once we have done that, we do not want to fall
// back to another flow anymore, and any error from there on is most likely
// a genuine authentication error.
await Promise.race([
allowFallbackIfFailed(server.listen()),
signalPromise,
]);
const authCodeFlowUrl = client.authorizationUrl({

@@ -240,3 +302,3 @@ scope,

// for the default `open` handler).
const browserStatePromise = new Promise((resolve, reject) => {
const browserStatePromise = allowFallbackIfFailed(new Promise((resolve, reject) => {
this.openBrowser({ url: localUrl, signal })

@@ -251,8 +313,8 @@ .then((browserHandle) => {

.catch(reject);
});
const timeout = new Promise((resolve, reject) => {
}));
const timeout = allowFallbackIfFailed(new Promise((resolve, reject) => {
if (this.options.openBrowserTimeout !== 0) {
setTimeout(reject, this.options.openBrowserTimeout ?? api_1.kDefaultOpenBrowserTimeout, new types_1.MongoDBOIDCError('Opening browser timed out')).unref();
}
});
}));
browserStatePromise.catch(() => {

@@ -270,20 +332,5 @@ /* squelch UnhandledPromiseRejectionWarning */

]);
// If we reached this point, we know that we have successfully opened
// a server listening on a local port and a browser and that the
// browser accessed the server. We do not want to fall back to another
// flow anymore, any error from here on is most likely a genuine
// authentication error.
enableFallback = false;
paramsUrl = await server.waitForOIDCParamsAndClose({ signal });
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
}
catch (err) {
// Tell the outer logic here to fallback to device auth flow if it is
// available if any of the steps above failed.
if (Object.isExtensible(err)) {
err[kEnableFallback] = enableFallback;
}
throw err;
}
finally {

@@ -320,2 +367,3 @@ await server.close();

async deviceAuthorizationFlow(state, signal) {
this.verifyValidUrl(state.serverOIDCMetadata, 'deviceAuthorizationEndpoint');
const { scope, issuerParams, clientParams } = this.getInitialOIDCIssuerAndClientParams(state.serverOIDCMetadata);

@@ -322,0 +370,0 @@ const issuer = new openid_client_1.Issuer(issuerParams);

@@ -161,2 +161,6 @@ "use strict";

this.oidcParamsPromise = new Promise((resolve, reject) => ([this.oidcParamsResolve, this.oidcParamsReject] = [resolve, reject]));
// Errors are handled by the caller of this instance.
this.oidcParamsPromise.catch(() => {
/* Suppress UnhandledPromiseRejectionWarning */
});
this.expressApp = (0, express_1.default)();

@@ -163,0 +167,0 @@ // Identity providers are not strictly required to use the query string to

@@ -45,2 +45,4 @@ /** @public */

}) => void;
'mongodb-oidc-plugin:refresh-started': () => void;
'mongodb-oidc-plugin:refresh-succeeded': () => void;
'mongodb-oidc-plugin:refresh-failed': (event: {

@@ -47,0 +49,0 @@ error: string;

@@ -21,3 +21,4 @@ /// <reference types="node" />

export declare function timeoutSignal(ms: number): AbortSignal;
export declare function withLock<T extends (...args: any[]) => Promise<any>>(fn: T): (...args: Parameters<T>) => ReturnType<T>;
export {};
//# sourceMappingURL=util.d.ts.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.timeoutSignal = exports.AbortSignal = exports.AbortController = exports.errorString = exports.withAbortCheck = exports.throwIfAborted = void 0;
exports.withLock = exports.timeoutSignal = exports.AbortSignal = exports.AbortController = exports.errorString = exports.withAbortCheck = exports.throwIfAborted = void 0;
class AbortError extends Error {

@@ -53,2 +53,20 @@ constructor() {

exports.timeoutSignal = timeoutSignal;
// Ensure that only one call to the target `fn` is active at a time.
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function withLock(fn) {
// `lock` represents the completion of the current call to fn(), if any.
let lock = Promise.resolve();
return (...args) => {
const result = lock
.then(() => fn(...args))
.finally(() => {
lock = Promise.resolve();
});
lock = result.catch(() => {
/* handled by caller */
});
return result;
};
}
exports.withLock = withLock;
//# sourceMappingURL=util.js.map

@@ -33,2 +33,11 @@ /// <reference types="node" />

/**
* Connect a log event emitter instance such as the one attached to a
* `MongoDBOIDCPlugin` instance to a log writer that follows the format
* provided by the `mongodb-log-writer` npm package.
*
* @public
*/
export declare function hookLoggerToMongoLogWriter(emitter: TypedEventEmitter<MongoDBOIDCLogEventsMap>, log: MongoLogWriter, contextPrefix: string): void;
/** @public */

@@ -87,2 +96,4 @@ export declare class MongoDBOIDCError extends Error {

}) => void;
'mongodb-oidc-plugin:refresh-started': () => void;
'mongodb-oidc-plugin:refresh-succeeded': () => void;
'mongodb-oidc-plugin:refresh-failed': (event: {

@@ -194,2 +205,10 @@ error: string;

/** @public */
export declare interface MongoLogWriter {
info(c: string, id: unknown, ctx: string, msg: string, attr?: unknown): void;
warn(c: string, id: unknown, ctx: string, msg: string, attr?: unknown): void;
error(c: string, id: unknown, ctx: string, msg: string, attr?: unknown): void;
mongoLogId(this: void, id: number): unknown;
}
/** @public */
export declare type OIDCAbortSignal = {

@@ -196,0 +215,0 @@ aborted: boolean;

@@ -16,3 +16,3 @@ {

"homepage": "https://github.com/mongodb-js/oidc-plugin",
"version": "0.1.0-alpha.0",
"version": "0.1.0-alpha.1",
"repository": {

@@ -77,2 +77,3 @@ "type": "git",

"mocha": "^10.2.0",
"mongodb-log-writer": "^1.1.5",
"node-fetch": "^3.3.1",

@@ -79,0 +80,0 @@ "nyc": "^15.1.0",

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc