diff --git a/e2e/autoscript-apps/src/authn-basic/autoscript.ts b/e2e/autoscript-apps/src/authn-basic/autoscript.ts index 8784a0efb..3735261db 100644 --- a/e2e/autoscript-apps/src/authn-basic/autoscript.ts +++ b/e2e/autoscript-apps/src/authn-basic/autoscript.ts @@ -19,6 +19,7 @@ function autoscript() { const amUrl = url.searchParams.get('amUrl') || 'https://auth.example.com:9443/am'; const realmPath = url.searchParams.get('realmPath') || 'root'; const un = url.searchParams.get('un') || 'sdkuser'; + const platformHeader = url.searchParams.get('platformHeader') === 'true' ? true : false; const pw = url.searchParams.get('pw') || 'password'; const tree = url.searchParams.get('tree') || 'UsernamePassword'; @@ -41,6 +42,7 @@ function autoscript() { next(); }, ], + platformHeader, realmPath, tree, serverConfig: { diff --git a/e2e/autoscript-suites/src/suites/authn-basic.lc.test.ts b/e2e/autoscript-suites/src/suites/authn-basic.lc.test.ts index 1f632e650..d352c5599 100644 --- a/e2e/autoscript-suites/src/suites/authn-basic.lc.test.ts +++ b/e2e/autoscript-suites/src/suites/authn-basic.lc.test.ts @@ -12,12 +12,14 @@ import { setupAndGo } from '../utilities/setup-and-go'; test.describe('Test Basic login flow', () => { test(`should login successfully and then log out`, async ({ browserName, page }) => { - const { messageArray } = await setupAndGo(page, browserName, 'authn-basic/'); + const { headerArray, messageArray } = await setupAndGo(page, browserName, 'authn-basic/'); // Test assertions expect(messageArray.includes('Basic login successful')).toBe(true); expect(messageArray.includes('Logout successful')).toBe(true); expect(messageArray.includes('Starting authentication with service')).toBe(true); expect(messageArray.includes('Continuing authentication with service')).toBe(true); + + expect(headerArray.find((headers) => headers.get('x-requested-platform'))).toBeFalsy(); }); }); diff --git a/e2e/autoscript-suites/src/suites/send-request-header.test.ts b/e2e/autoscript-suites/src/suites/send-request-header.test.ts index aea40283e..38805ad0d 100644 --- a/e2e/autoscript-suites/src/suites/send-request-header.test.ts +++ b/e2e/autoscript-suites/src/suites/send-request-header.test.ts @@ -16,6 +16,9 @@ test(`should verifies x-requested-platform header is present in the request`, as page, browserName, }) => { - const { networkArray } = await setupAndGo(page, browserName, 'authn-central-login/'); - expect(networkArray.includes('x-requested-platform')).toBe(true); + const { headerArray } = await setupAndGo(page, browserName, 'authn-basic/', { + platformHeader: 'true', + }); + + expect(headerArray.find((headers) => headers.get('x-requested-platform'))).toBeTruthy(); }); diff --git a/e2e/autoscript-suites/src/utilities/setup-and-go.ts b/e2e/autoscript-suites/src/utilities/setup-and-go.ts index 44be41f5c..804abe2d7 100644 --- a/e2e/autoscript-suites/src/utilities/setup-and-go.ts +++ b/e2e/autoscript-suites/src/utilities/setup-and-go.ts @@ -25,6 +25,7 @@ export async function setupAndGo( dialogInput?: string; email?: string; middleware?: string; + platformHeader?: string; preAuthenticated?: string; pw?: string; realmPath?: string; @@ -40,7 +41,12 @@ export async function setupAndGo( wellknown?: string; }, // eslint-disable-next-line @typescript-eslint/no-explicit-any -): Promise<{ messageArray: string[]; networkArray: string[] }> { +): Promise<{ + headerArray: Headers[]; + messageArray: string[]; + networkArray: string[]; +}> { + const headerArray: Headers[] = []; const messageArray: string[] = []; const networkArray: string[] = []; @@ -53,6 +59,7 @@ export async function setupAndGo( config && config.code && url.searchParams.set('code', (config && config.code) || ''); url.searchParams.set('email', (config && config.email) || ''); url.searchParams.set('middleware', (config && config.middleware) || ''); + url.searchParams.set('platformHeader', (config && config.platformHeader) || ''); url.searchParams.set('preAuthenticated', (config && config.preAuthenticated) || ''); url.searchParams.set('pw', (config && config.pw) || USERS[0].pw); url.searchParams.set('realmPath', (config && config.realmPath) || REALM_PATH); @@ -81,10 +88,11 @@ export async function setupAndGo( page.on('request', async (req) => { networkArray.push(`${new URL(req.url()).pathname}, ${req.resourceType()}`); + }); + page.on('request', async (req) => { const headers = await req.allHeaders(); - const headersKeys = Object.keys(headers); - networkArray.push(...headersKeys); + headerArray.push(new Headers(headers)); }); page.on('dialog', async (dialog) => { @@ -93,5 +101,5 @@ export async function setupAndGo( await page.waitForSelector(config?.selector || '.Test_Complete'); - return { messageArray, networkArray }; + return { headerArray, messageArray, networkArray }; } diff --git a/packages/javascript-sdk/src/auth/index.ts b/packages/javascript-sdk/src/auth/index.ts index 3fe3927fa..682f06b55 100644 --- a/packages/javascript-sdk/src/auth/index.ts +++ b/packages/javascript-sdk/src/auth/index.ts @@ -30,7 +30,7 @@ abstract class Auth { * @return {Step} The next step in the authentication tree. */ public static async next(previousStep?: Step, options?: StepOptions): Promise { - const { middleware, realmPath, serverConfig, tree, type } = Config.get(options); + const { middleware, platformHeader, realmPath, serverConfig, tree, type } = Config.get(options); const query = options ? options.query : {}; const url = this.constructUrl(serverConfig, realmPath, tree, query); const runMiddleware = middlewareWrapper( @@ -47,6 +47,25 @@ abstract class Auth { }, ); const req = runMiddleware(middleware); + + /** + * Run after as to now allow mutation by user + * Since the init headers can be an array, object or Headers class, + * we need to handle all types. + */ + if (platformHeader) { + if (req.init.headers instanceof Headers) { + req.init.headers.set('X-Requested-Platform', X_REQUESTED_PLATFORM); + } else if (Array.isArray(req.init.headers)) { + req.init.headers.push(['X-Requested-Platform', X_REQUESTED_PLATFORM]); + } else if (req.init.headers) { + req.init.headers['X-Requested-Platform'] = X_REQUESTED_PLATFORM; + } else { + req.init.headers = { + 'X-Requested-Platform': X_REQUESTED_PLATFORM, + }; + } + } const res = await withTimeout(fetch(req.url.toString(), req.init), serverConfig.timeout); const json = await this.getResponseJson(res); return json; @@ -75,10 +94,10 @@ abstract class Auth { 'Accept-API-Version': 'protocol=1.0,resource=2.1', 'Content-Type': 'application/json', 'X-Requested-With': REQUESTED_WITH, - 'X-Requested-Platform': X_REQUESTED_PLATFORM, }), method: 'POST', }; + return init; } diff --git a/packages/javascript-sdk/src/config/index.ts b/packages/javascript-sdk/src/config/index.ts index 88f82294a..5530bfd5b 100644 --- a/packages/javascript-sdk/src/config/index.ts +++ b/packages/javascript-sdk/src/config/index.ts @@ -28,6 +28,7 @@ function setDefaults(options: ConfigOptions): ConfigOptions { ...options, oauthThreshold: options.oauthThreshold || DEFAULT_OAUTH_THRESHOLD, logLevel: options.logLevel || 'none', + platformHeader: options.platformHeader || false, prefix: options.prefix || PREFIX, }; } diff --git a/packages/javascript-sdk/src/config/interfaces.ts b/packages/javascript-sdk/src/config/interfaces.ts index dd0a790a2..57c97757a 100644 --- a/packages/javascript-sdk/src/config/interfaces.ts +++ b/packages/javascript-sdk/src/config/interfaces.ts @@ -19,6 +19,7 @@ interface Action { // eslint-disable-next-line @typescript-eslint/no-explicit-any payload: any; } + /** * Custom Logger for logger */ @@ -50,6 +51,7 @@ interface ConfigOptions { oauthThreshold?: number; logLevel?: LogLevel; logger?: LoggerFunctions; + platformHeader?: boolean; prefix?: string; } diff --git a/packages/javascript-sdk/src/session-manager/index.ts b/packages/javascript-sdk/src/session-manager/index.ts index 7c9239f7e..8a65903a7 100644 --- a/packages/javascript-sdk/src/session-manager/index.ts +++ b/packages/javascript-sdk/src/session-manager/index.ts @@ -25,13 +25,12 @@ abstract class SessionManager { * Ends the current session. */ public static async logout(options?: ConfigOptions): Promise { - const { middleware, realmPath, serverConfig } = Config.get(options); + const { middleware, platformHeader, realmPath, serverConfig } = Config.get(options); const init: RequestInit = { credentials: 'include', headers: new Headers({ 'Accept-API-Version': 'protocol=1.0,resource=2.0', 'X-Requested-With': REQUESTED_WITH, - 'X-Requested-Platform': X_REQUESTED_PLATFORM, }), method: 'POST', }; @@ -44,6 +43,26 @@ abstract class SessionManager { { type: ActionTypes.Logout }, ); const req = runMiddleware(middleware); + + /** + * Run after as to now allow mutation by user + * Since the init headers can be an array, object or Headers class, + * we need to handle all types. + */ + if (platformHeader) { + if (req.init.headers instanceof Headers) { + req.init.headers.set('X-Requested-Platform', X_REQUESTED_PLATFORM); + } else if (Array.isArray(req.init.headers)) { + req.init.headers.push(['X-Requested-Platform', X_REQUESTED_PLATFORM]); + } else if (req.init.headers) { + req.init.headers['X-Requested-Platform'] = X_REQUESTED_PLATFORM; + } else { + req.init.headers = { + 'X-Requested-Platform': X_REQUESTED_PLATFORM, + }; + } + } + const response = await withTimeout(fetch(req.url.toString(), req.init), serverConfig.timeout); if (!isOkOr4xx(response)) { throw new Error(`Failed to log out; received ${response.status}`);