From 296c818ddf1206b0f1565d97159bf146b42e3a73 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 11 Aug 2023 16:19:13 +0200 Subject: [PATCH] auth-node: move parseWebPessageResponse to test util + fix error value handling Signed-off-by: Patrik Oldsberg --- .../__testUtils__/parseWebMessageResponse.ts | 28 ++++++++++++++++ .../src/flow/sendWebMessageResponse.test.ts | 33 ++++++++++++++----- .../src/flow/sendWebMessageResponse.ts | 8 ++++- .../oauth/createOAuthRouteHandlers.test.ts | 13 +------- 4 files changed, 60 insertions(+), 22 deletions(-) create mode 100644 plugins/auth-node/src/flow/__testUtils__/parseWebMessageResponse.ts diff --git a/plugins/auth-node/src/flow/__testUtils__/parseWebMessageResponse.ts b/plugins/auth-node/src/flow/__testUtils__/parseWebMessageResponse.ts new file mode 100644 index 0000000000..eda8469ac6 --- /dev/null +++ b/plugins/auth-node/src/flow/__testUtils__/parseWebMessageResponse.ts @@ -0,0 +1,28 @@ +/* + * Copyright 2023 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { WebMessageResponse } from '../sendWebMessageResponse'; + +export function parseWebMessageResponse(text: string): { + response: WebMessageResponse; + origin: string; +} { + const [response, origin] = text.matchAll(/decodeURIComponent\('(.+?)'\)/g); + return { + response: JSON.parse(decodeURIComponent(response[1])), + origin: decodeURIComponent(origin[1]), + }; +} diff --git a/plugins/auth-node/src/flow/sendWebMessageResponse.test.ts b/plugins/auth-node/src/flow/sendWebMessageResponse.test.ts index fd706159ba..cc192638f2 100644 --- a/plugins/auth-node/src/flow/sendWebMessageResponse.test.ts +++ b/plugins/auth-node/src/flow/sendWebMessageResponse.test.ts @@ -20,6 +20,7 @@ import { sendWebMessageResponse, type WebMessageResponse, } from './sendWebMessageResponse'; +import { parseWebMessageResponse } from './__testUtils__/parseWebMessageResponse'; describe('oauth helpers', () => { describe('safelyEncodeURIComponent', () => { @@ -78,7 +79,12 @@ describe('oauth helpers', () => { type: 'authorization_response', error: new Error('Unknown error occurred'), }; - const encoded = safelyEncodeURIComponent(JSON.stringify(data)); + const encoded = safelyEncodeURIComponent( + JSON.stringify({ + type: 'authorization_response', + error: { name: 'Error', message: 'Unknown error occurred' }, + }), + ); sendWebMessageResponse(mockResponse, appOrigin, data); expect(mockResponse.setHeader).toHaveBeenCalledTimes(3); @@ -122,20 +128,29 @@ describe('oauth helpers', () => { }, }; sendWebMessageResponse(mockResponse, appOrigin, data); - expect(responseBody.match(/.postMessage\(/g)).toHaveLength(2); - expect( - responseBody.match(/.postMessage\([a-zA-z.()]*, \'\*\'\)/g), - ).toHaveLength(1); + expect(parseWebMessageResponse(responseBody).response).toEqual({ + type: 'authorization_response', + response: { + providerInfo: expect.any(Object), + profile: { + email: 'foo@bar.com', + }, + backstageIdentity: expect.any(Object), + }, + }); const errData: WebMessageResponse = { type: 'authorization_response', error: new Error('Unknown error occurred'), }; sendWebMessageResponse(mockResponse, appOrigin, errData); - expect(responseBody.match(/.postMessage\(/g)).toHaveLength(2); - expect( - responseBody.match(/.postMessage\([a-zA-z.()]*, \'\*\'\)/g), - ).toHaveLength(1); + expect(parseWebMessageResponse(responseBody).response).toEqual({ + type: 'authorization_response', + error: { + name: 'Error', + message: 'Unknown error occurred', + }, + }); }); it('handles single quotes and unicode chars safely', () => { diff --git a/plugins/auth-node/src/flow/sendWebMessageResponse.ts b/plugins/auth-node/src/flow/sendWebMessageResponse.ts index 7bb658bd11..4c7a85854d 100644 --- a/plugins/auth-node/src/flow/sendWebMessageResponse.ts +++ b/plugins/auth-node/src/flow/sendWebMessageResponse.ts @@ -17,6 +17,7 @@ import { Response } from 'express'; import crypto from 'crypto'; import { ClientAuthResponse } from '../types'; +import { serializeError } from '@backstage/errors'; /** * Payload sent as a post message after the auth request is complete. @@ -47,7 +48,12 @@ export function sendWebMessageResponse( appOrigin: string, response: WebMessageResponse, ): void { - const jsonData = JSON.stringify(response); + const jsonData = JSON.stringify(response, (_, value) => { + if (value instanceof Error) { + return serializeError(value); + } + return value; + }); const base64Data = safelyEncodeURIComponent(jsonData); const base64Origin = safelyEncodeURIComponent(appOrigin); diff --git a/plugins/auth-node/src/oauth/createOAuthRouteHandlers.test.ts b/plugins/auth-node/src/oauth/createOAuthRouteHandlers.test.ts index 926d08fa77..2573d95ea1 100644 --- a/plugins/auth-node/src/oauth/createOAuthRouteHandlers.test.ts +++ b/plugins/auth-node/src/oauth/createOAuthRouteHandlers.test.ts @@ -24,8 +24,8 @@ import { createOAuthRouteHandlers } from './createOAuthRouteHandlers'; import { OAuthAuthenticator } from './types'; import { errorHandler } from '@backstage/backend-common'; import { encodeOAuthState, OAuthState } from './state'; -import { WebMessageResponse } from '../flow'; import { PassportProfile } from '../passport'; +import { parseWebMessageResponse } from '../flow/__testUtils__/parseWebMessageResponse'; const mockAuthenticator: jest.Mocked> = { initialize: jest.fn(_r => ({ ctx: 'authenticator' })), @@ -109,17 +109,6 @@ function getGrantedScopesCookie(test: SuperAgentTest) { }); } -function parseWebMessageResponse(text: string): { - response: WebMessageResponse; - origin: string; -} { - const [response, origin] = text.matchAll(/decodeURIComponent\('(.+?)'\)/g); - return { - response: JSON.parse(decodeURIComponent(response[1])), - origin: decodeURIComponent(origin[1]), - }; -} - describe('createOAuthRouteHandlers', () => { afterEach(() => jest.clearAllMocks());