diff --git a/src/cmap/auth/mongodb_oidc.ts b/src/cmap/auth/mongodb_oidc.ts index 4cab886112f..231862d2c65 100644 --- a/src/cmap/auth/mongodb_oidc.ts +++ b/src/cmap/auth/mongodb_oidc.ts @@ -4,11 +4,12 @@ import type { HandshakeDocument } from '../connect'; import type { Connection } from '../connection'; import { type AuthContext, AuthProvider } from './auth_provider'; import type { MongoCredentials } from './mongo_credentials'; -import { AzureMachineWorkflow } from './mongodb_oidc/azure_machine_workflow'; -import { GCPMachineWorkflow } from './mongodb_oidc/gcp_machine_workflow'; -import { K8SMachineWorkflow } from './mongodb_oidc/k8s_machine_workflow'; +import { AutomatedCallbackWorkflow } from './mongodb_oidc/automated_callback_workflow'; +import { callback as azureCallback } from './mongodb_oidc/azure_machine_workflow'; +import { callback as gcpCallback } from './mongodb_oidc/gcp_machine_workflow'; +import { callback as k8sCallback } from './mongodb_oidc/k8s_machine_workflow'; import { TokenCache } from './mongodb_oidc/token_cache'; -import { TokenMachineWorkflow } from './mongodb_oidc/token_machine_workflow'; +import { callback as testCallback } from './mongodb_oidc/token_machine_workflow'; /** Error when credentials are missing. */ const MISSING_CREDENTIALS_ERROR = 'AuthContext must provide credentials.'; @@ -78,6 +79,8 @@ export interface OIDCCallbackParams { idpInfo?: IdPInfo; /** The refresh token, if applicable, to be used by the callback to request a new token from the issuer. */ refreshToken?: string; + /** The token audience for GCP and Azure. */ + tokenAudience?: string; } /** @@ -93,6 +96,8 @@ type EnvironmentName = 'test' | 'azure' | 'gcp' | 'k8s' | undefined; /** @internal */ export interface Workflow { + cache: TokenCache; + /** * All device workflows must implement this method in order to get the access * token and then call authenticate with it. @@ -116,10 +121,10 @@ export interface Workflow { /** @internal */ export const OIDC_WORKFLOWS: Map Workflow> = new Map(); -OIDC_WORKFLOWS.set('test', () => new TokenMachineWorkflow(new TokenCache())); -OIDC_WORKFLOWS.set('azure', () => new AzureMachineWorkflow(new TokenCache())); -OIDC_WORKFLOWS.set('gcp', () => new GCPMachineWorkflow(new TokenCache())); -OIDC_WORKFLOWS.set('k8s', () => new K8SMachineWorkflow(new TokenCache())); +OIDC_WORKFLOWS.set('test', () => new AutomatedCallbackWorkflow(new TokenCache(), testCallback)); +OIDC_WORKFLOWS.set('azure', () => new AutomatedCallbackWorkflow(new TokenCache(), azureCallback)); +OIDC_WORKFLOWS.set('gcp', () => new AutomatedCallbackWorkflow(new TokenCache(), gcpCallback)); +OIDC_WORKFLOWS.set('k8s', () => new AutomatedCallbackWorkflow(new TokenCache(), k8sCallback)); /** * OIDC auth provider. diff --git a/src/cmap/auth/mongodb_oidc/automated_callback_workflow.ts b/src/cmap/auth/mongodb_oidc/automated_callback_workflow.ts index f98d87f6a27..3ecb7c3f7d0 100644 --- a/src/cmap/auth/mongodb_oidc/automated_callback_workflow.ts +++ b/src/cmap/auth/mongodb_oidc/automated_callback_workflow.ts @@ -66,6 +66,9 @@ export class AutomatedCallbackWorkflow extends CallbackWorkflow { if (credentials.username) { params.username = credentials.username; } + if (credentials.mechanismProperties.TOKEN_RESOURCE) { + params.tokenAudience = credentials.mechanismProperties.TOKEN_RESOURCE; + } const timeout = Timeout.expires(AUTOMATED_TIMEOUT_MS); try { return await Promise.race([this.executeAndValidateCallback(params), timeout]); diff --git a/src/cmap/auth/mongodb_oidc/azure_machine_workflow.ts b/src/cmap/auth/mongodb_oidc/azure_machine_workflow.ts index 1f41b8dc08d..5331fea6ed1 100644 --- a/src/cmap/auth/mongodb_oidc/azure_machine_workflow.ts +++ b/src/cmap/auth/mongodb_oidc/azure_machine_workflow.ts @@ -1,9 +1,7 @@ import { addAzureParams, AZURE_BASE_URL } from '../../../client-side-encryption/providers/azure'; import { MongoAzureError } from '../../../error'; import { get } from '../../../utils'; -import type { MongoCredentials } from '../mongo_credentials'; -import { type AccessToken, MachineWorkflow } from './machine_workflow'; -import { type TokenCache } from './token_cache'; +import type { OIDCCallbackFunction, OIDCCallbackParams, OIDCResponse } from '../mongodb_oidc'; /** Azure request headers. */ const AZURE_HEADERS = Object.freeze({ Metadata: 'true', Accept: 'application/json' }); @@ -17,39 +15,29 @@ const TOKEN_RESOURCE_MISSING_ERROR = 'TOKEN_RESOURCE must be set in the auth mechanism properties when ENVIRONMENT is azure.'; /** - * Device workflow implementation for Azure. - * - * @internal + * The callback function to be used in the automated callback workflow. + * @param params - The OIDC callback parameters. + * @returns The OIDC response. */ -export class AzureMachineWorkflow extends MachineWorkflow { - /** - * Instantiate the machine workflow. - */ - constructor(cache: TokenCache) { - super(cache); +export const callback: OIDCCallbackFunction = async ( + params: OIDCCallbackParams +): Promise => { + const tokenAudience = params.tokenAudience; + const username = params.username; + if (!tokenAudience) { + throw new MongoAzureError(TOKEN_RESOURCE_MISSING_ERROR); } - - /** - * Get the token from the environment. - */ - async getToken(credentials?: MongoCredentials): Promise { - const tokenAudience = credentials?.mechanismProperties.TOKEN_RESOURCE; - const username = credentials?.username; - if (!tokenAudience) { - throw new MongoAzureError(TOKEN_RESOURCE_MISSING_ERROR); - } - const response = await getAzureTokenData(tokenAudience, username); - if (!isEndpointResultValid(response)) { - throw new MongoAzureError(ENDPOINT_RESULT_ERROR); - } - return response; + const response = await getAzureTokenData(tokenAudience, username); + if (!isEndpointResultValid(response)) { + throw new MongoAzureError(ENDPOINT_RESULT_ERROR); } -} + return response; +}; /** * Hit the Azure endpoint to get the token data. */ -async function getAzureTokenData(tokenAudience: string, username?: string): Promise { +async function getAzureTokenData(tokenAudience: string, username?: string): Promise { const url = new URL(AZURE_BASE_URL); addAzureParams(url, tokenAudience, username); const response = await get(url, { @@ -62,8 +50,8 @@ async function getAzureTokenData(tokenAudience: string, username?: string): Prom } const result = JSON.parse(response.body); return { - access_token: result.access_token, - expires_in: Number(result.expires_in) + accessToken: result.access_token, + expiresInSeconds: Number(result.expires_in) }; } @@ -77,9 +65,9 @@ function isEndpointResultValid( ): token is { access_token: unknown; expires_in: unknown } { if (token == null || typeof token !== 'object') return false; return ( - 'access_token' in token && - typeof token.access_token === 'string' && - 'expires_in' in token && - typeof token.expires_in === 'number' + 'accessToken' in token && + typeof token.accessToken === 'string' && + 'expiresInSeconds' in token && + typeof token.expiresInSeconds === 'number' ); } diff --git a/src/cmap/auth/mongodb_oidc/gcp_machine_workflow.ts b/src/cmap/auth/mongodb_oidc/gcp_machine_workflow.ts index 6b8c1ee0541..c55f1d5bb7d 100644 --- a/src/cmap/auth/mongodb_oidc/gcp_machine_workflow.ts +++ b/src/cmap/auth/mongodb_oidc/gcp_machine_workflow.ts @@ -1,8 +1,6 @@ import { MongoGCPError } from '../../../error'; import { get } from '../../../utils'; -import { type MongoCredentials } from '../mongo_credentials'; -import { type AccessToken, MachineWorkflow } from './machine_workflow'; -import { type TokenCache } from './token_cache'; +import type { OIDCCallbackFunction, OIDCCallbackParams, OIDCResponse } from '../mongodb_oidc'; /** GCP base URL. */ const GCP_BASE_URL = @@ -15,30 +13,25 @@ const GCP_HEADERS = Object.freeze({ 'Metadata-Flavor': 'Google' }); const TOKEN_RESOURCE_MISSING_ERROR = 'TOKEN_RESOURCE must be set in the auth mechanism properties when ENVIRONMENT is gcp.'; -export class GCPMachineWorkflow extends MachineWorkflow { - /** - * Instantiate the machine workflow. - */ - constructor(cache: TokenCache) { - super(cache); - } - - /** - * Get the token from the environment. - */ - async getToken(credentials?: MongoCredentials): Promise { - const tokenAudience = credentials?.mechanismProperties.TOKEN_RESOURCE; - if (!tokenAudience) { - throw new MongoGCPError(TOKEN_RESOURCE_MISSING_ERROR); - } - return await getGcpTokenData(tokenAudience); +/** + * The callback function to be used in the automated callback workflow. + * @param params - The OIDC callback parameters. + * @returns The OIDC response. + */ +export const callback: OIDCCallbackFunction = async ( + params: OIDCCallbackParams +): Promise => { + const tokenAudience = params.tokenAudience; + if (!tokenAudience) { + throw new MongoGCPError(TOKEN_RESOURCE_MISSING_ERROR); } -} + return await getGcpTokenData(tokenAudience); +}; /** * Hit the GCP endpoint to get the token data. */ -async function getGcpTokenData(tokenAudience: string): Promise { +async function getGcpTokenData(tokenAudience: string): Promise { const url = new URL(GCP_BASE_URL); url.searchParams.append('audience', tokenAudience); const response = await get(url, { @@ -49,5 +42,5 @@ async function getGcpTokenData(tokenAudience: string): Promise { `Status code ${response.status} returned from the GCP endpoint. Response body: ${response.body}` ); } - return { access_token: response.body }; + return { accessToken: response.body }; } diff --git a/src/cmap/auth/mongodb_oidc/k8s_machine_workflow.ts b/src/cmap/auth/mongodb_oidc/k8s_machine_workflow.ts index 22dc9cb9f62..1df15926b10 100644 --- a/src/cmap/auth/mongodb_oidc/k8s_machine_workflow.ts +++ b/src/cmap/auth/mongodb_oidc/k8s_machine_workflow.ts @@ -1,7 +1,6 @@ import { readFile } from 'fs/promises'; -import { type AccessToken, MachineWorkflow } from './machine_workflow'; -import { type TokenCache } from './token_cache'; +import type { OIDCCallbackFunction, OIDCResponse } from '../mongodb_oidc'; /** The fallback file name */ const FALLBACK_FILENAME = '/var/run/secrets/kubernetes.io/serviceaccount/token'; @@ -12,27 +11,20 @@ const AZURE_FILENAME = 'AZURE_FEDERATED_TOKEN_FILE'; /** The AWS environment variable for the file name. */ const AWS_FILENAME = 'AWS_WEB_IDENTITY_TOKEN_FILE'; -export class K8SMachineWorkflow extends MachineWorkflow { - /** - * Instantiate the machine workflow. - */ - constructor(cache: TokenCache) { - super(cache); +/** + * The callback function to be used in the automated callback workflow. + * @param params - The OIDC callback parameters. + * @returns The OIDC response. + */ +export const callback: OIDCCallbackFunction = async (): Promise => { + let filename: string; + if (process.env[AZURE_FILENAME]) { + filename = process.env[AZURE_FILENAME]; + } else if (process.env[AWS_FILENAME]) { + filename = process.env[AWS_FILENAME]; + } else { + filename = FALLBACK_FILENAME; } - - /** - * Get the token from the environment. - */ - async getToken(): Promise { - let filename: string; - if (process.env[AZURE_FILENAME]) { - filename = process.env[AZURE_FILENAME]; - } else if (process.env[AWS_FILENAME]) { - filename = process.env[AWS_FILENAME]; - } else { - filename = FALLBACK_FILENAME; - } - const token = await readFile(filename, 'utf8'); - return { access_token: token }; - } -} + const token = await readFile(filename, 'utf8'); + return { accessToken: token }; +}; diff --git a/src/cmap/auth/mongodb_oidc/machine_workflow.ts b/src/cmap/auth/mongodb_oidc/machine_workflow.ts deleted file mode 100644 index b666335ec0c..00000000000 --- a/src/cmap/auth/mongodb_oidc/machine_workflow.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { setTimeout } from 'timers/promises'; - -import { type Document } from '../../../bson'; -import { ns } from '../../../utils'; -import type { Connection } from '../../connection'; -import type { MongoCredentials } from '../mongo_credentials'; -import type { Workflow } from '../mongodb_oidc'; -import { finishCommandDocument } from './command_builders'; -import { type TokenCache } from './token_cache'; - -/** The time to throttle callback calls. */ -const THROTTLE_MS = 100; - -/** - * The access token format. - * @internal - */ -export interface AccessToken { - access_token: string; - expires_in?: number; -} - -/** @internal */ -export type OIDCTokenFunction = (credentials: MongoCredentials) => Promise; - -/** - * Common behaviour for OIDC machine workflows. - * @internal - */ -export abstract class MachineWorkflow implements Workflow { - cache: TokenCache; - callback: OIDCTokenFunction; - lastExecutionTime: number; - - /** - * Instantiate the machine workflow. - */ - constructor(cache: TokenCache) { - 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: Connection, credentials: MongoCredentials): Promise { - const token = await this.getTokenFromCacheOrEnv(connection, credentials); - const command = finishCommandDocument(token); - await connection.command(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: Connection, credentials: MongoCredentials): Promise { - 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: Connection, credentials: MongoCredentials): Promise { - // 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 = finishCommandDocument(token); - document.db = credentials.source; - return { speculativeAuthenticate: document }; - } - - /** - * Get the token from the cache or environment. - */ - private async getTokenFromCacheOrEnv( - connection: Connection, - credentials: MongoCredentials - ): Promise { - 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. - */ - private withLock(callback: OIDCTokenFunction): OIDCTokenFunction { - let lock: Promise = Promise.resolve(); - return async (credentials: MongoCredentials): Promise => { - // 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 setTimeout(THROTTLE_MS - difference); - } - this.lastExecutionTime = Date.now(); - return await callback(credentials); - }); - return await lock; - }; - } - - /** - * Get the token from the environment or endpoint. - */ - abstract getToken(credentials: MongoCredentials): Promise; -} diff --git a/src/cmap/auth/mongodb_oidc/token_machine_workflow.ts b/src/cmap/auth/mongodb_oidc/token_machine_workflow.ts index de32c469594..340be227453 100644 --- a/src/cmap/auth/mongodb_oidc/token_machine_workflow.ts +++ b/src/cmap/auth/mongodb_oidc/token_machine_workflow.ts @@ -1,34 +1,21 @@ import * as fs from 'fs'; import { MongoAWSError } from '../../../error'; -import { type AccessToken, MachineWorkflow } from './machine_workflow'; -import { type TokenCache } from './token_cache'; +import type { OIDCCallbackFunction, OIDCResponse } from '../mongodb_oidc'; /** Error for when the token is missing in the environment. */ const TOKEN_MISSING_ERROR = 'OIDC_TOKEN_FILE must be set in the environment.'; /** - * Device workflow implementation for AWS. - * - * @internal + * The callback function to be used in the automated callback workflow. + * @param params - The OIDC callback parameters. + * @returns The OIDC response. */ -export class TokenMachineWorkflow extends MachineWorkflow { - /** - * Instantiate the machine workflow. - */ - constructor(cache: TokenCache) { - super(cache); +export const callback: OIDCCallbackFunction = async (): Promise => { + const tokenFile = process.env.OIDC_TOKEN_FILE; + if (!tokenFile) { + throw new MongoAWSError(TOKEN_MISSING_ERROR); } - - /** - * Get the token from the environment. - */ - async getToken(): Promise { - const tokenFile = process.env.OIDC_TOKEN_FILE; - if (!tokenFile) { - throw new MongoAWSError(TOKEN_MISSING_ERROR); - } - const token = await fs.promises.readFile(tokenFile, 'utf8'); - return { access_token: token }; - } -} + const token = await fs.promises.readFile(tokenFile, 'utf8'); + return { accessToken: token }; +}; diff --git a/test/mongodb.ts b/test/mongodb.ts index f94a511929c..d9ffc4c0a11 100644 --- a/test/mongodb.ts +++ b/test/mongodb.ts @@ -121,9 +121,6 @@ export * from '../src/cmap/auth/mongodb_aws'; export * from '../src/cmap/auth/mongodb_oidc'; export * from '../src/cmap/auth/mongodb_oidc/azure_machine_workflow'; export * from '../src/cmap/auth/mongodb_oidc/callback_workflow'; -export * from '../src/cmap/auth/mongodb_oidc/gcp_machine_workflow'; -export * from '../src/cmap/auth/mongodb_oidc/machine_workflow'; -export * from '../src/cmap/auth/mongodb_oidc/token_machine_workflow'; export * from '../src/cmap/auth/plain'; export * from '../src/cmap/auth/providers'; export * from '../src/cmap/auth/scram'; diff --git a/test/unit/cmap/auth/mongodb_oidc/azure_machine_workflow.test.ts b/test/unit/cmap/auth/mongodb_oidc/azure_machine_workflow.test.ts index b60c4f045da..b61bbd62465 100644 --- a/test/unit/cmap/auth/mongodb_oidc/azure_machine_workflow.test.ts +++ b/test/unit/cmap/auth/mongodb_oidc/azure_machine_workflow.test.ts @@ -1,20 +1,20 @@ import { expect } from 'chai'; -import * as sinon from 'sinon'; // eslint-disable-next-line @typescript-eslint/no-restricted-imports -import { TokenCache } from '../../../../../src/cmap/auth/mongodb_oidc/token_cache'; -import { AzureMachineWorkflow, Connection, MongoCredentials } from '../../../../mongodb'; - -describe('AzureMachineFlow', function () { - describe('#execute', function () { - const workflow = new AzureMachineWorkflow(new TokenCache()); +import { callback } from '../../../../../src/cmap/auth/mongodb_oidc/azure_machine_workflow'; +import { OIDC_VERSION, type OIDCCallbackParams } from '../../../../mongodb'; +describe('Azure machine workflow', function () { + describe('#callback', function () { context('when TOKEN_RESOURCE is not set', function () { - const connection = sinon.createStubInstance(Connection); - const credentials = sinon.createStubInstance(MongoCredentials); + const controller = new AbortController(); + const params: OIDCCallbackParams = { + timeoutContext: controller.signal, + version: OIDC_VERSION + }; it('throws an error', async function () { - const error = await workflow.execute(connection, credentials).catch(error => error); + const error = await callback(params).catch(error => error); expect(error.message).to.include('TOKEN_RESOURCE'); }); }); diff --git a/test/unit/cmap/auth/mongodb_oidc/gcp_machine_workflow.test.ts b/test/unit/cmap/auth/mongodb_oidc/gcp_machine_workflow.test.ts index 48d66f49d82..f99465cb1f7 100644 --- a/test/unit/cmap/auth/mongodb_oidc/gcp_machine_workflow.test.ts +++ b/test/unit/cmap/auth/mongodb_oidc/gcp_machine_workflow.test.ts @@ -1,46 +1,22 @@ import { expect } from 'chai'; -import * as sinon from 'sinon'; // eslint-disable-next-line @typescript-eslint/no-restricted-imports -import { TokenCache } from '../../../../../src/cmap/auth/mongodb_oidc/token_cache'; -import { Connection, GCPMachineWorkflow, MongoCredentials } from '../../../../mongodb'; - -describe('GCPMachineFlow', function () { - describe('#execute', function () { - const workflow = new GCPMachineWorkflow(new TokenCache()); +import { callback } from '../../../../../src/cmap/auth/mongodb_oidc/gcp_machine_workflow'; +import { OIDC_VERSION, type OIDCCallbackParams } from '../../../../mongodb'; +describe('GCP machine workflow', function () { + describe('#callback', function () { context('when TOKEN_RESOURCE is not set', function () { - const connection = sinon.createStubInstance(Connection); - const credentials = sinon.createStubInstance(MongoCredentials); + const controller = new AbortController(); + const params: OIDCCallbackParams = { + timeoutContext: controller.signal, + version: OIDC_VERSION + }; it('throws an error', async function () { - const error = await workflow.execute(connection, credentials).catch(error => error); + const error = await callback(params).catch(error => error); expect(error.message).to.include('TOKEN_RESOURCE'); }); }); }); - - describe('#getTokenFromCacheOrEnv', function () { - context('when the cache has a token', function () { - const connection = sinon.createStubInstance(Connection); - const credentials = sinon.createStubInstance(MongoCredentials); - - context('when the connection has no token', function () { - let cache; - let workflow; - - this.beforeEach(function () { - cache = new TokenCache(); - cache.put({ accessToken: 'test', expiresInSeconds: 7200 }); - workflow = new GCPMachineWorkflow(cache); - }); - - it('sets the token on the connection', async function () { - const token = await workflow.getTokenFromCacheOrEnv(connection, credentials); - expect(token).to.equal('test'); - expect(connection.accessToken).to.equal('test'); - }); - }); - }); - }); }); diff --git a/test/unit/cmap/auth/mongodb_oidc/token_machine_workflow.test.ts b/test/unit/cmap/auth/mongodb_oidc/token_machine_workflow.test.ts index b0302d7f03e..624c37dbb27 100644 --- a/test/unit/cmap/auth/mongodb_oidc/token_machine_workflow.test.ts +++ b/test/unit/cmap/auth/mongodb_oidc/token_machine_workflow.test.ts @@ -1,18 +1,12 @@ import { expect } from 'chai'; -import * as sinon from 'sinon'; // eslint-disable-next-line @typescript-eslint/no-restricted-imports -import { TokenCache } from '../../../../../src/cmap/auth/mongodb_oidc/token_cache'; -import { Connection, MongoCredentials, TokenMachineWorkflow } from '../../../../mongodb'; - -describe('TokenMachineFlow', function () { - describe('#execute', function () { - const workflow = new TokenMachineWorkflow(new TokenCache()); +import { callback } from '../../../../../src/cmap/auth/mongodb_oidc/token_machine_workflow'; +describe('Token machine workflow', function () { + describe('#callback', function () { context('when OIDC_TOKEN_FILE is not in the env', function () { let file; - const connection = sinon.createStubInstance(Connection); - const credentials = sinon.createStubInstance(MongoCredentials); before(function () { file = process.env.OIDC_TOKEN_FILE; @@ -26,7 +20,7 @@ describe('TokenMachineFlow', function () { }); it('throws an error', async function () { - const error = await workflow.execute(connection, credentials).catch(error => error); + const error = await callback().catch(error => error); expect(error.message).to.include('OIDC_TOKEN_FILE'); }); });