Skip to content

Commit 1ec0dde

Browse files
authored
Merge pull request #2 from Gaurav0/use_postmessage_instead_of_storage_event_embedded_case
[TP-179336] Use postmessage instead of storage event for embedded case
2 parents 24705d0 + 154ec01 commit 1ec0dde

4 files changed

Lines changed: 197 additions & 6 deletions

File tree

addon/mixins/ui-service-mixin.js

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ var ServicesMixin = Mixin.create({
3838
let service = this;
3939
let lastRemote = this.remote;
4040
let storageToriiEventHandler;
41+
let messageToriiEventHandler;
4142

4243
return new EmberPromise(function (resolve, reject) {
4344
if (lastRemote) {
@@ -55,7 +56,34 @@ var ServicesMixin = Mixin.create({
5556
});
5657
}
5758
};
59+
60+
// Using postMessage as an alternative to localStorage/storageEvent
61+
// for case of web site embedded in iframe
62+
messageToriiEventHandler = function (messageEvent) {
63+
if (messageEvent.data === 'getPendingRequestKey') {
64+
messageEvent.source.postMessage(
65+
JSON.stringify({ pendingRequestKey: service.pendingRequestKey }),
66+
window.location.origin
67+
);
68+
} else {
69+
const msg = JSON.parse(messageEvent.data);
70+
const key = Object.keys(msg)[0];
71+
var remoteIdFromEvent = PopupIdSerializer.deserialize(
72+
decodeURIComponent(key)
73+
);
74+
if (remoteId === remoteIdFromEvent) {
75+
var data = parseMessage(msg[key], keys);
76+
localStorage.removeItem(key);
77+
run(function () {
78+
resolve(data);
79+
});
80+
}
81+
}
82+
};
83+
window.addEventListener('message', messageToriiEventHandler);
84+
5885
var pendingRequestKey = PopupIdSerializer.serialize(remoteId);
86+
service.pendingRequestKey = pendingRequestKey;
5987
localStorage.setItem(CURRENT_REQUEST_KEY, pendingRequestKey);
6088
localStorage.removeItem(WARNING_KEY);
6189

@@ -115,6 +143,7 @@ var ServicesMixin = Mixin.create({
115143
}).finally(function () {
116144
// didClose will reject this same promise, but it has already resolved.
117145
service.close();
146+
window.removeEventListener('message', messageToriiEventHandler);
118147
window.removeEventListener('storage', storageToriiEventHandler);
119148
});
120149
},

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "torii",
3-
"version": "1.0.0-beta.2",
3+
"version": "1.0.0-beta.3",
44
"description": "A set of clean abstractions for authentication in Ember.js",
55
"keywords": [
66
"authentication",

public/redirect.html

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,49 @@
44
<meta charset="UTF-8">
55
<title>Torii OAuth Redirect</title>
66
<script>
7-
var CURRENT_REQUEST_KEY = '__torii_request';
8-
var pendingRequestKey = window.localStorage.getItem(CURRENT_REQUEST_KEY);
7+
const CURRENT_REQUEST_KEY = '__torii_request';
8+
let pendingRequestKey = window.localStorage.getItem(CURRENT_REQUEST_KEY);
9+
const origin = window.location.origin;
10+
let opener = window.opener;
11+
try {
12+
if (opener && opener.parent && opener.parent.origin === origin) {
13+
opener = opener.parent;
14+
}
15+
} catch {}
916

1017
if (pendingRequestKey) {
1118
window.localStorage.removeItem(CURRENT_REQUEST_KEY);
12-
var url = window.location.toString();
19+
const url = window.location.toString();
1320
window.localStorage.setItem(pendingRequestKey, url);
14-
}
21+
const obj = {};
22+
obj[pendingRequestKey] = url;
23+
opener?.postMessage(
24+
JSON.stringify(obj),
25+
origin
26+
);
27+
28+
window.close();
29+
} else {
30+
// localStorage not shared with opener due to browser restrictions
31+
opener?.postMessage('getPendingRequestKey', origin);
32+
window.addEventListener('message', function(messageEvent) {
33+
if (messageEvent.source === opener) {
34+
const msg = JSON.parse(messageEvent.data);
35+
if (msg.pendingRequestKey) {
36+
pendingRequestKey = msg.pendingRequestKey;
37+
url = window.location.toString();
38+
const obj = {};
39+
obj[pendingRequestKey] = url;
40+
opener.postMessage(
41+
JSON.stringify(obj),
42+
origin
43+
);
1544

16-
window.close();
45+
window.close();
46+
}
47+
}
48+
});
49+
}
1750
</script>
1851
</head>
1952
</html>
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/* eslint-disable ember/no-mixins, ember/no-new-mixins, ember/no-classic-classes */
2+
3+
import EmberObject from '@ember/object';
4+
import Evented from '@ember/object/evented';
5+
import UiServiceMixin, {
6+
CURRENT_REQUEST_KEY,
7+
} from 'torii/mixins/ui-service-mixin';
8+
import PopupIdSerializer from 'torii/lib/popup-id-serializer';
9+
import { module, test } from 'qunit';
10+
11+
module('Unit | Mixin | ui-service-mixin', function (hooks) {
12+
const originalWindowOpen = window.open;
13+
14+
const popupId = '09123-asdf';
15+
const expectedUrl = 'http://authServer';
16+
const expectedRedirectUrl = 'http://localserver?code=fr';
17+
const expectedMessage = 'getPendingRequestKey';
18+
19+
const mockWindowListener = (event) => {
20+
let msg;
21+
try {
22+
msg = JSON.parse(event.data);
23+
} catch {
24+
// allow
25+
}
26+
if (msg && Object.keys(msg)[0] === 'pendingRequestKey') {
27+
const obj = {};
28+
const key = PopupIdSerializer.serialize(encodeURIComponent(popupId));
29+
obj[key] = `${expectedUrl}?redirect_url=${expectedRedirectUrl}`;
30+
window.dispatchEvent(
31+
new MessageEvent('message', {
32+
data: JSON.stringify(obj),
33+
source: window,
34+
})
35+
);
36+
}
37+
};
38+
39+
const buildMockWindow = function (windowName) {
40+
windowName = windowName || '';
41+
window.addEventListener('message', mockWindowListener);
42+
return {
43+
name: windowName,
44+
focus() {},
45+
close() {},
46+
open() {
47+
this.postMessage(expectedMessage);
48+
},
49+
postMessage(msg) {
50+
window.dispatchEvent(
51+
new MessageEvent('message', { data: msg, source: window })
52+
);
53+
},
54+
};
55+
};
56+
57+
const buildPopupIdGenerator = function (popupId) {
58+
return {
59+
generate() {
60+
return popupId;
61+
},
62+
};
63+
};
64+
65+
let Popup = EmberObject.extend(Evented, UiServiceMixin, {
66+
// Open a popup window.
67+
openRemote(_url, pendingRequestKey) {
68+
this.remote = buildMockWindow(pendingRequestKey);
69+
this.remote.open();
70+
},
71+
72+
closeRemote() {
73+
this.remote.closed = true;
74+
},
75+
76+
pollRemote() {
77+
if (!this.remote) {
78+
return;
79+
}
80+
},
81+
});
82+
83+
let popup;
84+
85+
hooks.beforeEach(function () {
86+
popup = Popup.create({ remoteIdGenerator: buildPopupIdGenerator(popupId) });
87+
localStorage.removeItem(CURRENT_REQUEST_KEY);
88+
});
89+
90+
hooks.afterEach(async function () {
91+
localStorage.removeItem(CURRENT_REQUEST_KEY);
92+
window.open = originalWindowOpen;
93+
window.removeEventListener('message', mockWindowListener);
94+
popup.destroy();
95+
});
96+
97+
test('requests pending request key', function (assert) {
98+
assert.expect(1);
99+
100+
let resultMessage;
101+
const resultMessageListener = (event) => {
102+
resultMessage = event.data;
103+
};
104+
try {
105+
window.addEventListener('message', resultMessageListener);
106+
107+
popup.openRemote(expectedUrl, CURRENT_REQUEST_KEY);
108+
109+
assert.strictEqual(
110+
resultMessage,
111+
expectedMessage,
112+
'requests pendingRequestKey'
113+
);
114+
} finally {
115+
window.removeEventListener('message', resultMessageListener);
116+
}
117+
});
118+
119+
test('returns data after receiving key', async function (assert) {
120+
const keys = ['redirect_url'];
121+
const result = await popup.open(expectedUrl, keys);
122+
123+
assert.strictEqual(
124+
result.redirect_url,
125+
expectedRedirectUrl,
126+
'returns data successfully'
127+
);
128+
});
129+
});

0 commit comments

Comments
 (0)