From 789057a7569e286b4c690acde8931517566ced65 Mon Sep 17 00:00:00 2001 From: Gaurav Munjal Date: Thu, 19 Sep 2024 16:17:39 -0400 Subject: [PATCH 1/4] bump version --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9e6cccc9..ff47b7ff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "torii", - "version": "1.0.0-beta.2", + "version": "1.0.0-beta.3", "description": "A set of clean abstractions for authentication in Ember.js", "keywords": [ "authentication", From 4b11c1cfc20a6e3d4647b5a2a4eafc7ca5b05e50 Mon Sep 17 00:00:00 2001 From: Gaurav Munjal Date: Tue, 23 Jul 2024 09:37:33 -0400 Subject: [PATCH 2/4] Use postmessage instead of storage event for embedded case --- addon/mixins/ui-service-mixin.js | 31 ++++++++++++++++++++++++++ public/redirect.html | 37 +++++++++++++++++++++++++++----- 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/addon/mixins/ui-service-mixin.js b/addon/mixins/ui-service-mixin.js index b58a7284..b098ff2e 100644 --- a/addon/mixins/ui-service-mixin.js +++ b/addon/mixins/ui-service-mixin.js @@ -38,6 +38,7 @@ var ServicesMixin = Mixin.create({ let service = this; let lastRemote = this.remote; let storageToriiEventHandler; + let messageToriiEventHandler; return new EmberPromise(function (resolve, reject) { if (lastRemote) { @@ -55,7 +56,36 @@ var ServicesMixin = Mixin.create({ }); } }; + + // Using postMessage as an alternative to localStorage/storageEvent + // for case of web site embedded in iframe + messageToriiEventHandler = function (messageEvent) { + if (messageEvent.source === service.remote) { + if (messageEvent.data === 'getPendingRequestKey') { + service.remote.postMessage( + JSON.stringify({ pendingRequestKey: service.pendingRequestKey }), + window.location.origin + ); + } else { + const msg = JSON.parse(messageEvent.data); + const key = Object.keys(msg)[0]; + var remoteIdFromEvent = PopupIdSerializer.deserialize( + decodeURIComponent(key) + ); + if (remoteId === remoteIdFromEvent) { + var data = parseMessage(msg[key], keys); + localStorage.removeItem(key); + run(function () { + resolve(data); + }); + } + } + } + }; + window.addEventListener('message', messageToriiEventHandler); + var pendingRequestKey = PopupIdSerializer.serialize(remoteId); + service.pendingRequestKey = pendingRequestKey; localStorage.setItem(CURRENT_REQUEST_KEY, pendingRequestKey); localStorage.removeItem(WARNING_KEY); @@ -115,6 +145,7 @@ var ServicesMixin = Mixin.create({ }).finally(function () { // didClose will reject this same promise, but it has already resolved. service.close(); + window.removeEventListener('message', messageToriiEventHandler); window.removeEventListener('storage', storageToriiEventHandler); }); }, diff --git a/public/redirect.html b/public/redirect.html index b58b7a26..0babb523 100644 --- a/public/redirect.html +++ b/public/redirect.html @@ -4,16 +4,43 @@ Torii OAuth Redirect From 470525d2c897169e6d89e994f3050298eb41a4b8 Mon Sep 17 00:00:00 2001 From: Gaurav Munjal Date: Tue, 17 Sep 2024 10:10:20 -0400 Subject: [PATCH 3/4] fix for feature flag off case --- addon/mixins/ui-service-mixin.js | 36 +++++++++++++++----------------- public/redirect.html | 14 +++++++++---- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/addon/mixins/ui-service-mixin.js b/addon/mixins/ui-service-mixin.js index b098ff2e..604dd12c 100644 --- a/addon/mixins/ui-service-mixin.js +++ b/addon/mixins/ui-service-mixin.js @@ -60,25 +60,23 @@ var ServicesMixin = Mixin.create({ // Using postMessage as an alternative to localStorage/storageEvent // for case of web site embedded in iframe messageToriiEventHandler = function (messageEvent) { - if (messageEvent.source === service.remote) { - if (messageEvent.data === 'getPendingRequestKey') { - service.remote.postMessage( - JSON.stringify({ pendingRequestKey: service.pendingRequestKey }), - window.location.origin - ); - } else { - const msg = JSON.parse(messageEvent.data); - const key = Object.keys(msg)[0]; - var remoteIdFromEvent = PopupIdSerializer.deserialize( - decodeURIComponent(key) - ); - if (remoteId === remoteIdFromEvent) { - var data = parseMessage(msg[key], keys); - localStorage.removeItem(key); - run(function () { - resolve(data); - }); - } + if (messageEvent.data === 'getPendingRequestKey') { + messageEvent.source.postMessage( + JSON.stringify({ pendingRequestKey: service.pendingRequestKey }), + window.location.origin + ); + } else { + const msg = JSON.parse(messageEvent.data); + const key = Object.keys(msg)[0]; + var remoteIdFromEvent = PopupIdSerializer.deserialize( + decodeURIComponent(key) + ); + if (remoteId === remoteIdFromEvent) { + var data = parseMessage(msg[key], keys); + localStorage.removeItem(key); + run(function () { + resolve(data); + }); } } }; diff --git a/public/redirect.html b/public/redirect.html index 0babb523..cda41a69 100644 --- a/public/redirect.html +++ b/public/redirect.html @@ -7,6 +7,12 @@ const CURRENT_REQUEST_KEY = '__torii_request'; let pendingRequestKey = window.localStorage.getItem(CURRENT_REQUEST_KEY); const origin = window.location.origin; + let opener = window.opener; + try { + if (opener && opener.parent && opener.parent.origin === origin) { + opener = opener.parent; + } + } catch {} if (pendingRequestKey) { window.localStorage.removeItem(CURRENT_REQUEST_KEY); @@ -14,7 +20,7 @@ window.localStorage.setItem(pendingRequestKey, url); const obj = {}; obj[pendingRequestKey] = url; - window.opener?.postMessage( + opener?.postMessage( JSON.stringify(obj), origin ); @@ -22,16 +28,16 @@ window.close(); } else { // localStorage not shared with opener due to browser restrictions - window.opener.postMessage('getPendingRequestKey', origin); + opener?.postMessage('getPendingRequestKey', origin); window.addEventListener('message', function(messageEvent) { - if (messageEvent.source === window.opener) { + if (messageEvent.source === opener) { const msg = JSON.parse(messageEvent.data); if (msg.pendingRequestKey) { pendingRequestKey = msg.pendingRequestKey; url = window.location.toString(); const obj = {}; obj[pendingRequestKey] = url; - window.opener.postMessage( + opener.postMessage( JSON.stringify(obj), origin ); From 154ec017293dc73f78029e46098c47276733a82e Mon Sep 17 00:00:00 2001 From: Gaurav Munjal Date: Mon, 23 Sep 2024 11:03:07 -0400 Subject: [PATCH 4/4] add test for postmessage in ui-service-mixin --- tests/unit/mixins/ui-service-mixin-test.js | 129 +++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 tests/unit/mixins/ui-service-mixin-test.js diff --git a/tests/unit/mixins/ui-service-mixin-test.js b/tests/unit/mixins/ui-service-mixin-test.js new file mode 100644 index 00000000..178f50ce --- /dev/null +++ b/tests/unit/mixins/ui-service-mixin-test.js @@ -0,0 +1,129 @@ +/* eslint-disable ember/no-mixins, ember/no-new-mixins, ember/no-classic-classes */ + +import EmberObject from '@ember/object'; +import Evented from '@ember/object/evented'; +import UiServiceMixin, { + CURRENT_REQUEST_KEY, +} from 'torii/mixins/ui-service-mixin'; +import PopupIdSerializer from 'torii/lib/popup-id-serializer'; +import { module, test } from 'qunit'; + +module('Unit | Mixin | ui-service-mixin', function (hooks) { + const originalWindowOpen = window.open; + + const popupId = '09123-asdf'; + const expectedUrl = 'http://authServer'; + const expectedRedirectUrl = 'http://localserver?code=fr'; + const expectedMessage = 'getPendingRequestKey'; + + const mockWindowListener = (event) => { + let msg; + try { + msg = JSON.parse(event.data); + } catch { + // allow + } + if (msg && Object.keys(msg)[0] === 'pendingRequestKey') { + const obj = {}; + const key = PopupIdSerializer.serialize(encodeURIComponent(popupId)); + obj[key] = `${expectedUrl}?redirect_url=${expectedRedirectUrl}`; + window.dispatchEvent( + new MessageEvent('message', { + data: JSON.stringify(obj), + source: window, + }) + ); + } + }; + + const buildMockWindow = function (windowName) { + windowName = windowName || ''; + window.addEventListener('message', mockWindowListener); + return { + name: windowName, + focus() {}, + close() {}, + open() { + this.postMessage(expectedMessage); + }, + postMessage(msg) { + window.dispatchEvent( + new MessageEvent('message', { data: msg, source: window }) + ); + }, + }; + }; + + const buildPopupIdGenerator = function (popupId) { + return { + generate() { + return popupId; + }, + }; + }; + + let Popup = EmberObject.extend(Evented, UiServiceMixin, { + // Open a popup window. + openRemote(_url, pendingRequestKey) { + this.remote = buildMockWindow(pendingRequestKey); + this.remote.open(); + }, + + closeRemote() { + this.remote.closed = true; + }, + + pollRemote() { + if (!this.remote) { + return; + } + }, + }); + + let popup; + + hooks.beforeEach(function () { + popup = Popup.create({ remoteIdGenerator: buildPopupIdGenerator(popupId) }); + localStorage.removeItem(CURRENT_REQUEST_KEY); + }); + + hooks.afterEach(async function () { + localStorage.removeItem(CURRENT_REQUEST_KEY); + window.open = originalWindowOpen; + window.removeEventListener('message', mockWindowListener); + popup.destroy(); + }); + + test('requests pending request key', function (assert) { + assert.expect(1); + + let resultMessage; + const resultMessageListener = (event) => { + resultMessage = event.data; + }; + try { + window.addEventListener('message', resultMessageListener); + + popup.openRemote(expectedUrl, CURRENT_REQUEST_KEY); + + assert.strictEqual( + resultMessage, + expectedMessage, + 'requests pendingRequestKey' + ); + } finally { + window.removeEventListener('message', resultMessageListener); + } + }); + + test('returns data after receiving key', async function (assert) { + const keys = ['redirect_url']; + const result = await popup.open(expectedUrl, keys); + + assert.strictEqual( + result.redirect_url, + expectedRedirectUrl, + 'returns data successfully' + ); + }); +});