49

I just found out that window.opener is not available in a window opened via window.open if the new URL is cross-domain, in IE. How do I detect window opener in IE

This will happen if the window starts in my domain, leaves it, and then comes back to my domain. I am attempting to have a social signup ( facebook, google, etc ) in the popup. When it completes it should close the new window and redirect the opener.

I know that Soundcloud is pulling this off, but I have no idea how. I see the URL change from theirs to Facebook, and then close.

After redirecting back to my site from 3rd party I run this:

var data = {
  type : 'complete',
  destination : '<?= $destination; ?>'
};
if ( window.opener ) {
  window.opener.postMessage( JSON.stringify( data ), '*' );
  window.close();
}
else {
  alert( "Unable to find window" );
}

It alerts out in IE, even though the window was originally my domain, which then redirected to FB, then redirected back to me. I thought may since I open my site and redirect immediately from PHP that may be an issue. However even when I opened my site, did window.location.href = 'facebookssite.com' it still complained when returning.

NOTE

Social signups do not work for google, FB, etc within an iframe. I believe they disallow them for security reasons.

Community
  • 1
  • 1
Dave Stein
  • 8,653
  • 13
  • 56
  • 104

6 Answers6

52

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.

Sapphire_Brick
  • 1,560
  • 12
  • 26
noseratio
  • 59,932
  • 34
  • 208
  • 486
  • @Noseratio: Your solution works great if other domain's url is opened in window but how can we modify your code to handle situation where internally Facebook authenticates the user and simply open the redirected url in window. In that case leftDomain flag is false, even though there is some internal redirection happened. – dark_shadow Oct 09 '15 at 06:11
  • @dark_shadow, I suggest you ask this as a separate question and link this one to it. – noseratio Oct 09 '15 at 08:17
  • @Noseratio, I have created a separate question http://stackoverflow.com/questions/33038950/how-to-close-an-authentication-pop-up-window-having-a-cross-domain-url Can you please take a look at it ? Thanks – dark_shadow Oct 09 '15 at 13:00
  • In internet explorer Edge I still get a permission denied exception after the third party site redireted back to my domain. Anyone else has this with Edge browser? – ThdK Apr 05 '17 at 13:14
  • 1
    Works for me in IE11. Haven't tested in Edge yet. – Jonast92 Aug 31 '18 at 15:13
  • @noseratio, Hi, Andrew! Can you please take a look at this? - https://stackoverflow.com/questions/58732237/oauth-popup-react – Arthur Nov 06 '19 at 14:21
  • 1
    @Arthur, I have only a limited experience with React, but I don't see why this wouldn't work. In 2019, I'd just use `async/await` and perhaps something like [`CancellablePromise`](https://codereview.stackexchange.com/questions/207116/extending-native-javascript-promise-with-cancellation-support) to do the status polling for the child window. – noseratio Nov 06 '19 at 19:40
  • Do you mind using one brace style consistently, rather than using both the K&R and Allman styles? – Sapphire_Brick Nov 25 '20 at 18:35
  • 1
    @Sapphire_Brick, I certainly don't and I hope my style has improved since 2013! Feel free to [edit the answer](https://stackoverflow.com/posts/18804255/edit) to make it look better and I'll accept the edit. – noseratio Nov 25 '20 at 22:20
17

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();
Khanh TO
  • 48,509
  • 13
  • 99
  • 115
  • I named my window reference `social_window`. `social_window.beforeunload = function() {` never fired anything. `social_window.onbeforeunload = function() {` fired only if I closed the window before my redirect to FB/Google occured. – Dave Stein Sep 10 '13 at 14:00
  • @Dave Stein: how about using a timer? This solution is not nice as we cannot have a nice solution because this is browser's behavior. All we can do is trying to find a workaround. – Khanh TO Sep 10 '13 at 15:37
  • @Dave Stein: I think the viable solution is to load the authentication form inside an iframe if possible. With this approach, we don't have to leave our domain and avoid this problem completely. – Khanh TO Sep 10 '13 at 15:41
  • It's not possible for an iframe answer. Updated my question... I accidentally removed that line when I edited it last. The interval won't work because even if I know it closed, I won't have the data from the opened window. – Dave Stein Sep 10 '13 at 16:18
  • @Dave Stein: could you try local storage? It should work but I'm afraid it does not work on all browsers (there are browsers that don't support local storage). – Khanh TO Sep 11 '13 at 06:06
  • Is it possible to read `window.data` cross-domain? 'cause if it is not - what's the point? Is there one? After all one could simply redirect the parent by changing `window.opener.location` directly. – jayarjo Aug 14 '19 at 17:25
  • @jayarjo: when you are redirected back to your domain, you can read `window.data` (not cross-domain anymore), but the problem is `window.opener` was removed previously which makes it impossible to change `window.opener.location` directly – Khanh TO Aug 17 '19 at 10:40
  • I use `window.opener.location` approach at the moment (cross-domain), 'cause among all the dirty workaround it turned to be the cleanest. Not sure what you mean by `window.opener was removed`. – jayarjo Aug 17 '19 at 10:45
  • @jayarjo: did you do redirect in your opened window to another domain? If so, it should be removed which was the problem faced in the question – Khanh TO Aug 17 '19 at 10:47
  • It won't be removed, because it turns out to be a typical usage scenario and browser makers are hesitant to touch it. – jayarjo Aug 17 '19 at 11:32
  • @jayarjo: it could be that things have changed after nearly 6 years. As you can see in the question: `This will happen if the window starts in my domain, leaves it, and then comes back to my domain` and https://stackoverflow.com/questions/7120534/window-opener-is-null-after-redirect/7120602 – Khanh TO Aug 17 '19 at 11:45
  • @KhanhTO, hi! :) Can you please check this question? - https://stackoverflow.com/q/58732237/9464680 – Arthur Nov 06 '19 at 14:23
  • I found stepping through each option extremely helpful. beforeunload was promising until it turned out it became undefined after navigating back to my domain. Ultimately the localStorage and storage event was the best solution for me. Thank you @KhanhTO – LoJo Jan 14 '21 at 01:41
1
  1. From your iframe, webpage, on yoursite.com ... open a new window on yoursite.com
  2. The window redirects itself to Google, Twitter, whatever
  3. Once done, the OAuth redirect returns the window to a page on yoursite.com
  4. The new window, because it has the same origin as the page that opened it, can communicate via window.open
Ben Vinegar
  • 307
  • 1
  • 8
  • How will the new window, communicate with its opener? `window.open( 'mysite.com', 'parent' )`? I would think parent would not be real there. Or do I just have to make sure the opener has an ID for target. I'm already doing 1-3 – Dave Stein Sep 05 '13 at 18:27
  • Don't use postmessage. Just use direct access, which you can do because they have same origin. `window.opener.globalFuncInOpenerContext()`. – Ben Vinegar Sep 05 '13 at 18:29
  • 1
    I updated my question. It isn't even knowing `window.opener` is real after returning to my site. I wonder what could be different that it works no issue for you. – Dave Stein Sep 05 '13 at 18:37
  • Your example still tries to call postMessage. Just to clarify, `window.opener.postMessage` is not the same as `window.opener.anActualFunctionInOpenerContext`. Here's the relevant code from our in-production app, from the final redirected-from-Twitter/Facebook/Google page: https://gist.github.com/benvinegar/5c8e96bc7f58a8c85f4d – Ben Vinegar Sep 05 '13 at 21:17
  • Granted you are correct that I will need to use a global, the fact that `window.opener` is not there is preventing me from doing anything. I'm testing in IE10 right now. Once I can get `window.opener` to exist I will change over to a global function. In your gist I wouldn't pass the `if-statement` – Dave Stein Sep 05 '13 at 21:44
  • Also, my code doesn't even attempt to postMessage currently, it just spits off that alert I put in for debugging. – Dave Stein Sep 05 '13 at 21:47
  • In Edge browser, you 'll get permission denied when accessing properties of the child window after is has been redirected to another domain. Even after the third party page redirected back to your domain. Anyone has a workaround for Edge browser? – ThdK Apr 19 '17 at 13:54
1

You can use use window.postMessage(), which is provided for this exact scenario.

Explanation: https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage

John A
  • 21
  • 1
0

Use localStorage or IndexedDB to communicate between windows that are showing documents from the same domain but which don't have a reference to each other.

Simply have a high-speed timer checking for the data, saving another piece of data to acknowledge receipt and the other window can find it and close.

In short - you use localStorage to pass commands and can even have a library to do this and delete the commands once they are executed, and post the return values.

Gregory Magarshak
  • 1,883
  • 2
  • 25
  • 35
-1

In my company we've different domains and there's the case where the intranet's site must get the our public website (to finally get rid of the maintenance of duplicated data). Inspired in Ben Vinegar i've come to this solution simple solution avoiding the :

Call to domain webpage (in my case with the same name as the external one)

local 'getInfo.php'

<?php 
      $idSp = (isset($_GET['idSp'])?$_GET['idSp']:null);
      echo file_get_contents('http://192.168.1.10/folder/getInfo.php?idSp='.$idSp);
 ?>

External 'getInfo.php' return

 <?php  
    echo '<script>window.opener.manageDisplay('.$getRes.','.$isOK.');</script>';
    if($auto_close){ echo "<script>window.close();</script>"; }
  ?>
Jordi
  • 616
  • 2
  • 9
  • 16