Skip to content

Commit 9e2dc91

Browse files
Merge pull request #935 from l1b3r/popup-storage-event
Listen for storage to receive auth hash from popup
2 parents 4156226 + 8cb450e commit 9e2dc91

File tree

3 files changed

+52
-19
lines changed

3 files changed

+52
-19
lines changed

docs-src/silent-refresh.md

+13-2
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ This simple implementation within silent-refresh.html is sufficient in most case
6767
var checks = [/[\?|&|#]code=/, /[\?|&|#]error=/, /[\?|&|#]token=/, /[\?|&|#]id_token=/];
6868
6969
function isResponse(str) {
70-
var count = 0;
7170
if (!str) return false;
7271
for(var i=0; i<checks.length; i++) {
7372
if (str.match(checks[i])) return true;
@@ -77,12 +76,24 @@ This simple implementation within silent-refresh.html is sufficient in most case
7776
7877
var message = isResponse(location.hash) ? location.hash : '#' + location.search;
7978
80-
(window.opener || window.parent).postMessage(message, location.origin);
79+
if (window.parent && window.parent !== window) {
80+
// if loaded as an iframe during silent refresh
81+
window.parent.postMessage(message, location.origin);
82+
} else if (window.opener && window.opener !== window) {
83+
// if loaded as a popup during initial login
84+
window.opener.postMessage(message, location.origin);
85+
} else {
86+
// last resort for a popup which has been through redirects and can't use window.opener
87+
localStorage.setItem('auth_hash', message);
88+
localStorage.removeItem('auth_hash');
89+
}
8190
</script>
8291
</body>
8392
</html>
8493
```
94+
The above example checks if the message in the URL (either hash or query string) is indeed a message returned with a response from an authentication provider and not an arbitrary value and then attempts to forward this message to a parent widow either by `.parent` (when this html is loaded in an iframe as a result of silent refresh) or by `.opener` (when the html is loaded into a popup during initial login) or finally using a storage event (as a fallback for complex cases, e.g. initial login in a popup with a cross-domain auth provider).
8595

96+
8697
Please make sure that this file is copied to your output directory by your build task. When using the CLI you can define it as an asset for this. For this, you have to add the following line to the file ``.angular-cli.json``:
8798

8899
```JSON

projects/lib/src/oauth-service.ts

+27-15
Original file line numberDiff line numberDiff line change
@@ -1083,7 +1083,7 @@ export class OAuthService extends AuthConfig implements OnDestroy {
10831083
return this.initLoginFlowInPopup(options);
10841084
}
10851085

1086-
public initLoginFlowInPopup(options?: { height?: number; width?: number }) {
1086+
public initLoginFlowInPopup(options?: { height?: number; width?: number }): Promise<boolean> {
10871087
options = options || {};
10881088
return this.createLoginUrl(
10891089
null,
@@ -1105,6 +1105,21 @@ export class OAuthService extends AuthConfig implements OnDestroy {
11051105
this.calculatePopupFeatures(options)
11061106
);
11071107
let checkForPopupClosedTimer: any;
1108+
1109+
const tryLogin = (hash: string) => {
1110+
this.tryLogin({
1111+
customHashFragment: hash,
1112+
preventClearHashAfterLogin: true,
1113+
customRedirectUri: this.silentRefreshRedirectUri,
1114+
}).then(() => {
1115+
cleanup();
1116+
resolve(true);
1117+
}, err => {
1118+
cleanup();
1119+
reject(err);
1120+
});
1121+
};
1122+
11081123
const checkForPopupClosed = () => {
11091124
if (!windowRef || windowRef.closed) {
11101125
cleanup();
@@ -1122,6 +1137,7 @@ export class OAuthService extends AuthConfig implements OnDestroy {
11221137

11231138
const cleanup = () => {
11241139
window.clearInterval(checkForPopupClosedTimer);
1140+
window.removeEventListener('storage', storageListener);
11251141
window.removeEventListener('message', listener);
11261142
if (windowRef !== null) {
11271143
windowRef.close();
@@ -1133,26 +1149,22 @@ export class OAuthService extends AuthConfig implements OnDestroy {
11331149
const message = this.processMessageEventMessage(e);
11341150

11351151
if (message && message !== null) {
1136-
this.tryLogin({
1137-
customHashFragment: message,
1138-
preventClearHashAfterLogin: true,
1139-
customRedirectUri: this.silentRefreshRedirectUri
1140-
}).then(
1141-
() => {
1142-
cleanup();
1143-
resolve();
1144-
},
1145-
err => {
1146-
cleanup();
1147-
reject(err);
1148-
}
1149-
);
1152+
window.removeEventListener('storage', storageListener);
1153+
tryLogin(message);
11501154
} else {
11511155
console.log('false event firing');
11521156
}
11531157
};
11541158

1159+
const storageListener = (event: StorageEvent) => {
1160+
if (event.key === 'auth_hash') {
1161+
window.removeEventListener('message', listener);
1162+
tryLogin(event.newValue);
1163+
}
1164+
};
1165+
11551166
window.addEventListener('message', listener);
1167+
window.addEventListener('storage', storageListener);
11561168
});
11571169
});
11581170
}

projects/sample/src/silent-refresh.html

+12-2
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
];
1010

1111
function isResponse(str) {
12-
var count = 0;
1312
if (!str) return false;
1413
for (var i = 0; i < checks.length; i++) {
1514
if (str.match(checks[i])) return true;
@@ -21,7 +20,18 @@
2120
? location.hash
2221
: '#' + location.search;
2322

24-
(window.opener || window.parent).postMessage(message, location.origin);
23+
if (window.parent && window.parent !== window) {
24+
// if loaded as an iframe during silent refresh
25+
window.parent.postMessage(message, location.origin);
26+
} else if (window.opener && window.opener !== window) {
27+
// if loaded as a popup during initial login
28+
window.opener.postMessage(message, location.origin);
29+
} else {
30+
// last resort for a popup which has been through redirects and can't use window.opener
31+
localStorage.setItem('auth_hash', message);
32+
localStorage.removeItem('auth_hash');
33+
}
34+
2535
</script>
2636
</body>
2737
</html>

0 commit comments

Comments
 (0)