Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add window message listener to auth-callback template #129

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

staxmanade
Copy link
Contributor

@staxmanade staxmanade commented Jan 8, 2017

My Hope:

You can tell me there's a better way... but if not...

What is this?

  • Wrapping everything in a clojure - because habit.
  • Adding a cross-window message handler for a specific case.

Context/Ramblings: (Copied and modified from superlogin pouchdb slack channel)

Ok, I had everything working nice and neato - but the moment I turned on https it backfired…

Using superlogin and superlogin-client to implement in the browser Facebook oauth, it used to work great when my site was on an http only connection.

The moment I stuck a cloudfront https configuration in front of it the following scenario started failing…

After completing my OAuth login - the redirection back to my site: https://mysite.com/auth/facebook/callback/code=... returns this page: https://github.com/colinskow/superlogin/blob/master/templates/oauth/auth-callback.ejs in a popup window.

But when that page tries to run it raises the following error:

Uncaught DOMException: Blocked a frame with origin "https://myapp.com" from accessing a cross-origin

on this line: https://github.com/colinskow/superlogin/blob/master/templates/oauth/auth-callback.ejs#L15

I found this StackOverlow post which talks about tackling the problem possibly another way: http://stackoverflow.com/questions/18625733/how-do-i-get-around-window-opener-cross-domain-security

I gave it a shot and was able to work-around my issue.

I believe that this code should work as it used to, while also enabling this possible new approach.

Below is the (ugly) client-side code used to request the token:


let shouldRetry = true;

window.addEventListener("message", function(ev) {
  console.log("message", ev.data.message, ev);
    if (ev.data.message === "superlogin-deliverResult") {
        console.log("result: " + ev.data.result);
        var error = ev.data.result.error;
        var session = ev.data.result.session;
        var link = ev.data.result.link;
        shouldRetry = false;
        window.superlogin.oauthSession(error, session, link)
        ev.source.close();
    }
});

let beginMonitoringOAuthWindow = () => {
  console.log('beginMonitoringOAuthWindow');
  let child = window.superlogin._oauthWindow;

  if (!child) {
    console.error("could not find superlogoin child window reference on", window.superlogin);
    return;
  }

  var leftDomain = true;
  var retryCount = 0;
  var interval = setInterval(function() {
      console.log('beginMonitoringOAuthWindow setInterval');
      try {
          if (child.document.domain === document.domain) {
              console.log('beginMonitoringOAuthWindow setInterval a', child.document.readyState);
              if (leftDomain && child.document.readyState === "complete") {
                  console.log('beginMonitoringOAuthWindow setInterval b');
                  // we're here when the child window returned to our domain
                  if (!shouldRetry || retryCount > 100) {
                    clearInterval(interval);
                  }
                  retryCount++;
                  console.log("returned: " + child.document.URL, child, child.document);
                  child.postMessage({ message: "superlogin-requestResult" }, "*");
              }
          } else {
              console.log('beginMonitoringOAuthWindow setInterval c');
              // this code should never be reached,
              // as the x-site security check throws
              // but just in case
              leftDomain = true;
          }
      } catch(e) {
          console.error(e);
          console.log('beginMonitoringOAuthWindow setInterval d');
          // we're here when the child window has been navigated away or closed
          if (child.closed) {
              console.log('beginMonitoringOAuthWindow setInterval e');
              clearInterval(interval);
              console.log("closed");
              return;
          }
          // navigated to another domain
          leftDomain = true;
          console.log('beginMonitoringOAuthWindow setInterval f');
      }
      console.log('beginMonitoringOAuthWindow setInterval g');
  }, 500);
};

Using it with superlogin-client

    // kick off the login promise
    var loginPromise = superlogin.socialAuth(providerType);

    // My crappy implementation above could return too fast - so wait a bit before starting the monitor.
    // don't start this up until after some time has passed???
    setTimeout(() => {
        beginMonitoringOAuthWindow();
    }, 2000);

    loginPromise.then(user => {
        console.log('login: user', user);
    });

@colinskow
Copy link
Owner

@staxmanade thank you very much for this. I have one question: is this a breaking change? In other words, will it work with the existing Javascript libraries or do they need to be updated?

@staxmanade
Copy link
Contributor Author

it should be backward compatable.

I was really hoping you'd say "silly boy, just change X in config and MAGIC"

For front ends to leverage it, they would have to post messages to it (like the example js posted).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants