How do I get around window.opener cross-domain security
- From your iframe, webpage, on yoursite.com ... open a new window on yoursite.com
- The window redirects itself to Google, Twitter, whatever
- Once done, the OAuth redirect returns the window to a page on yoursite.com
- The new window, because it has the same origin as the page that opened it, can communicate via window.open
Due to security reason, window.opener
is removed when redirecting to a different domain. The browser does not bother to restore the window.opener
when you're back. In your case, you could try:
1) Do your authentication inside an iframe if possible instead of using redirect.
2) In your case, I see that you need to post the data back to the parent window. You could try this instead:
In your opened window, just store your data
and close normally.
var data = {
type : 'complete',
destination : '<?= $destination; ?>'
};
window.hasData = true;
window.data = data;
window.close();
Your parent window has access to your opened window and can handle its close
event:
openedWindow.beforeunload = function (){
//here you could access this.data or openedWindow.data because you're on the same domain
if (this.hasData){
}
//Reason we have this check is because the beforeunload event fires whenever the user leaves your page for any reason including close, submit, clicking a link, ...
}
3) A workaround: Use a timer in your parent page to check for the closed
property of the openedWindow
setInterval(function(){
if (openedWindow.closed){
}
},1000);
4) Another solution using localStorage as you're on the same domain. You parent page can listen to the event
window.addEventListener("storage", function(event){
}, true);
Your openedWindow code:
var data = {
type : 'complete',
destination : '<?= $destination; ?>'
};
if (localStorage){
localStorage.setItem(JSON.stringify(data));
}
window.close();
Do it the other way around. Track the state of the child popup window from the main (opener) window, and you could easily know when the child window has been navigated back to you domain, so you could "talk" to it again. But don't close the child window by itself. Let the opener window obtain the result from the child window and then close it.
For example, main.html:
<!DOCTYPE html>
<head>
<title>main</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<script>
window.addEventListener("message", function(ev) {
if (ev.data.message === "deliverResult") {
alert("result: " + ev.data.result);
ev.source.close();
}
});
function Go() {
var child = window.open("child.html", "_blank", "height=200,width=200");
var leftDomain = false;
var interval = setInterval(function() {
try {
if (child.document.domain === document.domain) {
if (leftDomain && child.document.readyState === "complete") {
// we're here when the child window returned to our domain
clearInterval(interval);
alert("returned: " + child.document.URL);
child.postMessage({ message: "requestResult" }, "*");
}
}
else {
// this code should never be reached,
// as the x-site security check throws
// but just in case
leftDomain = true;
}
}
catch(e) {
// we're here when the child window has been navigated away or closed
if (child.closed) {
clearInterval(interval);
alert("closed");
return;
}
// navigated to another domain
leftDomain = true;
}
}, 500);
}
</script>
</head>
<body>
<button onclick="Go()">Go</button>
</body>
child.html:
<!DOCTYPE html>
<head>
<title>child</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<script>
window.addEventListener("message", function(ev) {
if (ev.data.message === "requestResult") {
// ev.source is the opener
ev.source.postMessage({ message: "deliverResult", result: true }, "*");
}
});
</script>
</head>
<body>
<a href="http://www.example.com">Go to example.com</a>
Then click the browser Back button when ready.
</body>
Tested with IE10.