postmessages to spawned window - wait for authentication

184 Views Asked by At

My application with the fake url just for our understanding www.myapp.com needs to spawn a popup window at the fake url www.myredirect.com.

Once reached www.myredirect.com the page is automatically redirected to a different url that we will call www.myfinalurl.com. Www.myredirect.com service makes sure to redirect the page to www.myfinalurl.com adding some query strings like www.myfinalurl.com?myparam=thisvalue.

I am the owner of www.myfinalurl.com and www.myapp.com, so I can code javascript in both.

From www.myapp.com I need to capture the query strings of www.myfinalurl.com included in the URL, however I cannot directly do that due to the cross origin policy, for this reason I will have to do it using postmessages.

However once opened the window at www.myredirect.com, the user might be asked to login, a process that takes a not-fixed amount of seconds, so I'll have to wait for the redirect to happen before proceeding. Furthermore, www.myapp.com is executed in sessions (www.myapp.com/s/mysession123), meaning that its url is not fixed.

The process usually is:

  • myapp.com sends a postmessage to the fixed url at myfinalurl.com
  • myapp.com starts listening to postmessages back
  • myfinalurl.com records the origin of the message received
  • myfinalurl.com sends back a postmessage containing its url
  • myapp.com receives the postmessage from myfinalurl and stops listening

However the problem here is that while the flow would usually work properly, in my case step 1 must be sent at the right time, however it will be lost, because the message will be sent to a not-existing web service when redirect still has to happen.

I am asking here if there is a possible workaround, and in your opinion what's the best workaround.

I am thinking that maybe I can send the initial postmessage from myapp.com to myfinalurl.com every x seconds, until I'll receive an answer back. Is this considered a decent practise?

All of the possible built in listeners seem to be blocked by cross origin policy.

Of course I tried asking to different AIs but didn't find answers yet.

This is my best attempt so far, waiting for 5 seconds before sending the message, meaning waiting for redirect to happen, however it doesn't work when asked to login.

myapp.com

const authPopup=window.open(url=passUrl, '_blank', 'width=640,height=480,menubar=no,toolbar=no');
const delay = ms => new Promise(res => setTimeout(res, ms)); 
const postMessageFunction = async () => { 
   await delay(5000); 
   popup.postMessage('origin','www.myfinalurl.com');
   window.addEventListener( 'messageBack', (event) => {
      const thisUrl = event.data;
      Shiny.setInputValue('UrlWithParams',thisUrl,{priority: 'event'});
      authPopup.close();
   }, false );
}; res=postMessageFunction();

www.myfinalurl.com

const mylocation = window.location.href;
window.addEventListener('origin', (event) => {
   event.source.postMessage( mylocation, event.origin, ); 
});
3

There are 3 best solutions below

0
Lajos Arpad On

You could postMessage to myfinalUrl, so myapp.com would be notified about myfinalUrl being loaded successfully. Afterwards you could send your message from myapp.com to myfinalUrl.

The issue with this is that you might open the same page at several times, so if you have n tabs where myapp.com and the popup being displayed and you open a new tab and start to open your popup, then you will need to make sure in the way you send the message that all your tabs and popups will know whether a message applies to them. You could use a variable or something for this purpose.

0
Kaiido On

If I get it properly, you don't need to send a message from myapp.com at all. Simply send a message from myfinalurl.com to its opener, passing myapp.com as the target origin:

opener.postMessage(location.search, "https://myapp.com");

The target origin parameter is enough to ensure that the browser will only deliver this message to myapp.com. Since you're the owner of that origin, I assume you trust it. The fact that the actual URL varies doesn't matter, the origin is still just https://myapp.com.
Then if you're doing something that could be dangerous with the received data, you may want to check in the received message that the source origin is indeed http://finalurl.com.

addEventListener("message", ({ data, origin }) => {
  if (origin === "https://myfinalurl.com") {
    doSomethingWithTheQueryString(data);
  }
});

However, beware that depending on how myredirect.com does redirect to your final URL, the link between both pages might be broken. For instance, if one day they use location.href = callbackURL, then you won't be able to access the opener anymore.

PS: From myapp.com, you may want to poll on the opened window to check its .closed property, and act appropriately for when the user dismisses the authentication process. Unfortunately, there is no event firing letting us know about this, so polling is the only way, but it doesn't need to be too intensive, a 2 or 3s interval is generally acceptable.

Outsourced Live example

Sources span on these three projects: app, redirect, finalurl.

2
ybouane On

if you're trying to nail the timing for when your popup is ready, and you've got control over the code on myfinalurl.com, you might consider telling that page to let myapp.com know it's ready.

One approach is to use the window.opener property from myfinalurl.com. This property refers to the window that opened the popup. You could set up a simple message back to the opener when myfinalurl.com has loaded completely, which includes after the redirect has happened. Here's a quick example:

On myfinalurl.com, you'd have something like this:

window.addEventListener('load', (event) => {
  window.opener.postMessage(window.location.href, 'www.myapp.com');
});

Then, on myapp.com, you must be prepared to handle this incoming message:

window.addEventListener('message', (event) => {
  if (event.origin === 'www.myfinalurl.com') {
    // Do something with event.data, which will be your final URL with the query params.
    Shiny.setInputValue('UrlWithParams', event.data, {priority: 'event'});
    authPopup.close();
  }
}, false);

This way, myapp.com won't need to keep sending messages hoping to hit the right time. Instead, myfinalurl.com takes on the job of sending a message when it's definitely ready to talk back.

Alternatively, another thing you can try is to detect when the original page receives the focus again (which usually happens after the popup/auth screen has closed).

window.addEventListener('focus', (event) => {
// Check if everything went alright.
});