localhost : postMessage target origin provided does not match the recipient window's origin

Viewed 4352

so i'm trying to build an application which uses the sso to authenticate users. here is the workflow :

  • start the app on localhost:3000 (i'm using a react single web application)
  • a popup will be shown (actually the popup will call my node js authentication route localhost:4000/authenticate which redirects the user to the sso authentication page)
  • after authentication the sso server will redirect the user to the node callback route (http://localhost:4000/authenticate/callback)
  • node checks if this is a valid user and returns a success message (actually node will send an html + javascript code to close the popup).
  • if the received message is success we gonna let the user load the app

here's some code :

App.js

 handleLogIn() {
    const msg = loginTab('http://localhost:4000/authenticate');
    msg.then(response => {
      console.log(response)
    });
  }

  render() {


    let loginButton = (<button onClick={this.handleLogIn.bind(this)}>Sign in</button>)

    return (
      <div>
        {loginButton}
      </div>
    )
  }

loginTab.js

const loginTab = (myUrl) => {
  const windowArea = {
    width: Math.floor(window.outerWidth * 0.8),
    height: Math.floor(window.outerHeight * 0.5),
  };

  if (windowArea.width < 1000) { windowArea.width = 1000; }
  if (windowArea.height < 630) { windowArea.height = 630; }
  windowArea.left = Math.floor(window.screenX + ((window.outerWidth - windowArea.width) / 2));
  windowArea.top = Math.floor(window.screenY + ((window.outerHeight - windowArea.height) / 8));

  const sep = (myUrl.indexOf('?') !== -1) ? '&' : '?';
  const url = `${myUrl}${sep}`;
  const windowOpts = `toolbar=0,scrollbars=1,status=1,resizable=1,location=1,menuBar=0,
    width=${windowArea.width},height=${windowArea.height},
    left=${windowArea.left},top=${windowArea.top}`;

  const authWindow = window.open(url, '_blank', windowOpts);
  // Create IE + others compatible event handler
  const eventMethod = window.addEventListener ? 'addEventListener' : 'attachEvent';
  const eventer = window[eventMethod];
  const messageEvent = eventMethod === 'attachEvent' ? 'onmessage' : 'message';

  // Listen to message from child window
  const authPromise = new Promise((resolve, reject) => {
    eventer(messageEvent, (msg) => {
      if (!~msg.origin.indexOf(`${window.location.protocol}//${window.location.host}`)) {
        authWindow.close();
        reject('Not allowed');
      }

      if (msg.data.payload) {
        try {
          resolve(JSON.parse(msg.data.payload));
        }
        catch(e) {
          resolve(msg.data.payload);
        }
        finally {
          authWindow.close();
        }
      } else {
        authWindow.close();
        reject('Unauthorised');
      }
    }, false);
  });

  return authPromise;
};

export default loginTab;

And this the node response :

Authentication.js

router.post(process.env.SAML_CALLBACK_PATH,
    function (req, res, next) {
        winston.debug('/Start authenticate callback ');
        next();
    },
    passport.authenticate('samlStrategy'),
    function (req, res, next) {

        winston.debug('Gsuite user successfully authenticated , email : %s', req.user.email)
        return res.sendFile(path.join(__dirname + '/success.html'));

    }
);

success.html

<!doctype html>
<html lang="fr">
<head>
  <title>Login successful</title>
</head>
<body>
  <h1>Success</h1>
  <p>You are authenticated...</p>
</body>
<script>
  document.body.onload = function() {

    console.log( window.opener.location)
    window.opener.postMessage(
      {
        status: 'success'
      },
      window.opener.location
    );
  };
</script>
</html>

The problem is that after the authentication i couldn't close the popup because of this error :

Failed to execute 'postMessage' on 'DOMWindow': The target origin provided ('http://localhost:4000') does not match the recipient window's origin ('http://localhost:3000').

I tried to change the window.opener.location in success.html to 'localhost:3000' and it works perfectly but it's not a good idea for a production environement.

1 Answers

Well, i tried many things and i solved my issue using this technic. In the development environement, i used a star to get it working (it's not a good practice).

window.opener.postMessage(
      {
        status: 'success'
      },
      '*'
    );

And in production, i used the real domain name and not localhost like this :

window.opener.postMessage(
      {
        status: 'success'
      },
      'http://my-server-domain:3000'
    );

Hope this will help someone.

Related