"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MachineWorkflow = void 0; const promises_1 = require("timers/promises"); const utils_1 = require("../../../utils"); const command_builders_1 = require("./command_builders"); /** The time to throttle callback calls. */ const THROTTLE_MS = 100; /** * Common behaviour for OIDC machine workflows. * @internal */ class MachineWorkflow { /** * Instantiate the machine workflow. */ constructor(cache) { this.cache = cache; this.callback = this.withLock(this.getToken.bind(this)); this.lastExecutionTime = Date.now() - THROTTLE_MS; } /** * Execute the workflow. Gets the token from the subclass implementation. */ async execute(connection, credentials) { const token = await this.getTokenFromCacheOrEnv(connection, credentials); const command = (0, command_builders_1.finishCommandDocument)(token); await connection.command((0, utils_1.ns)(credentials.source), command, undefined); } /** * Reauthenticate on a machine workflow just grabs the token again since the server * has said the current access token is invalid or expired. */ async reauthenticate(connection, credentials) { if (this.cache.hasAccessToken) { // Reauthentication implies the token has expired. if (connection.accessToken === this.cache.getAccessToken()) { // If connection's access token is the same as the cache's, remove // the token from the cache and connection. this.cache.removeAccessToken(); delete connection.accessToken; } else { // If the connection's access token is different from the cache's, set // the cache's token on the connection and do not remove from the // cache. connection.accessToken = this.cache.getAccessToken(); } } await this.execute(connection, credentials); } /** * Get the document to add for speculative authentication. */ async speculativeAuth(connection, credentials) { // The spec states only cached access tokens can use speculative auth. if (!this.cache.hasAccessToken) { return {}; } const token = await this.getTokenFromCacheOrEnv(connection, credentials); const document = (0, command_builders_1.finishCommandDocument)(token); document.db = credentials.source; return { speculativeAuthenticate: document }; } /** * Get the token from the cache or environment. */ async getTokenFromCacheOrEnv(connection, credentials) { if (this.cache.hasAccessToken) { const token = this.cache.getAccessToken(); // New connections won't have an access token so ensure we set here. if (!connection.accessToken) { connection.accessToken = token; } return token; } else { const token = await this.callback(credentials); this.cache.put({ accessToken: token.access_token, expiresInSeconds: token.expires_in }); // Put the access token on the connection as well. connection.accessToken = token.access_token; return token.access_token; } } /** * Ensure the callback is only executed one at a time, and throttled to * only once per 100ms. */ withLock(callback) { let lock = Promise.resolve(); return async (credentials) => { // We do this to ensure that we would never return the result of the // previous lock, only the current callback's value would get returned. await lock; lock = lock .catch(() => null) .then(async () => { const difference = Date.now() - this.lastExecutionTime; if (difference <= THROTTLE_MS) { await (0, promises_1.setTimeout)(THROTTLE_MS - difference); } this.lastExecutionTime = Date.now(); return await callback(credentials); }); return await lock; }; } } exports.MachineWorkflow = MachineWorkflow; //# sourceMappingURL=machine_workflow.js.map