add a postMessage with targetOrigin *

This commit is contained in:
jothisubaramaniam
2020-11-24 12:16:33 -05:00
parent d39ef4ecf5
commit e338648910
4 changed files with 122 additions and 3 deletions
@@ -81,6 +81,52 @@ describe('oauth helpers', () => {
expect(mockResponse.end).toBeCalledWith(expect.stringContaining(encoded));
});
it('should call postMessage twice but only one of them with target *', () => {
let responseBody: String;
const mockResponse = ({
end: jest.fn(function (body) {
responseBody = body;
return this;
}),
setHeader: jest.fn().mockReturnThis(),
} as unknown) as express.Response;
const data: WebMessageResponse = {
type: 'authorization_response',
response: {
providerInfo: {
accessToken: 'ACCESS_TOKEN',
idToken: 'ID_TOKEN',
expiresInSeconds: 10,
scope: 'email',
},
profile: {
email: 'foo@bar.com',
},
backstageIdentity: {
id: 'a',
idToken: 'a.b.c',
},
},
};
postMessageResponse(mockResponse, appOrigin, data);
expect(responseBody.match(/.postMessage\(/g)).toHaveLength(2);
expect(
responseBody.match(/.postMessage\([a-zA-z.()]*, \'\*\'\)/g),
).toHaveLength(1);
const errData: WebMessageResponse = {
type: 'authorization_response',
error: new Error('Unknown error occurred'),
};
postMessageResponse(mockResponse, appOrigin, errData);
expect(responseBody.match(/.postMessage\(/g)).toHaveLength(2);
expect(
responseBody.match(/.postMessage\([a-zA-z.()]*, \'\*\'\)/g),
).toHaveLength(1);
});
it('handles single quotes and unicode chars safely', () => {
const mockResponse = ({
end: jest.fn().mockReturnThis(),
@@ -38,10 +38,24 @@ export const postMessageResponse = (
// data.
// TODO: Make target app origin configurable globally
//
// postMessage fails silently if the targetOrigin is disallowed.
// So 2 postMessages are sent from the popup to the parent window.
// First, the origin being used to post the actual authorization response is
// shared with the parent window with a postMessage with targetOrigin '*'.
// Second, the actual authorization response is sent with the app origin
// as the targetOrigin.
// If the first message was received but the actual auth response was
// never received, the event listener can conclude that targetOrigin
// was disallowed, indicating potential misconfiguration.
//
const script = `
var json = decodeURIComponent('${base64Data}');
var authResponse = decodeURIComponent('${base64Data}');
var origin = decodeURIComponent('${base64Origin}');
(window.opener || window.parent).postMessage(JSON.parse(json), origin);
var originInfo = {'targetOrigin': origin};
(window.opener || window.parent).postMessage(originInfo, '*');
(window.opener || window.parent).postMessage(JSON.parse(authResponse), origin);
window.close();
`;
const hash = crypto.createHash('sha256').update(script).digest('base64');