3

I have made a website that enables users to create their own widgets and place them on their own websites. I want users of those websites to be able to log in to these widgets using Twitter, Facebook and Google. I have 99% of the process in place, but the remaining 1% is my stumbling block. The process I have implemented is as follows:

  1. User creates a widget on my site (mysite.com)
  2. User places some javascript on their own website (example.com) to embed the widget
  3. The user of a widget clicks "Log in with Twitter"
  4. A new window is opened (using window.open) which loads mysite.com/auth/twitter, redirecting them through the oAuth flow on twitter.com
  5. All being well, the user gets redirected back to mysite.com/auth/twitter/callback in the new window and I store the user's details in the database on mysite.com.

At this point, the newly-created User ID should be passed back to the main window in which the widget is embedded. But, as far as I can tell, there is no way to do so because because window.opener in the new window is null (due to the redirects that have happened). Nor do I have a reference from the main window to the new window, also due to the redirects.

I have tried window.postMessage from the new window to the main window, directly accessing functions and variables in the new window from the main window, all to no avail. There is seemingly no way to reference any data or properties from either window.

Is there anything else I can try or do I have to implement the process without a new window somehow?

If it matters, mysite.com is built in Laravel and the social authentication process uses Socialite.

Any help is appreciated!

b4tch
  • 959
  • 9
  • 16

4 Answers4

0

I have tried window.postMessage from the new window to the main window

postMessage works cross-domain, but you must in that case specify the second parameter (targetOrigin).

KaKi87
  • 915
  • 7
  • 15
  • It only works cross-domain if no redirects happen within the window. As soon as they do, the reference to `window.opener` is lost – b4tch Aug 02 '22 at 12:37
  • You don't need `window.opener` to do `postMessage`. – KaKi87 Aug 02 '22 at 13:22
  • Then how would the code look to use postMessage from the new window to the main window? Surely you need to tell the new window which window to post the message to? – b4tch Aug 02 '22 at 14:05
0

Taking into account your clarifications regarding how the widget is embedded, you are fairly limited in regards to communication options as you saw.

Since your child window now has no way to communicate with the parent, being without a window.opener reference and on a different domain, you could rely on the parent and have it track the child window progress like suggested here : https://stackoverflow.com/a/18804255/18706075

Also, not to be insistent on the iframe approach, but you could also add an invisible iframe opened to mysite.com. That way, the pop-up window that ends up on mysite.com has many options to make data available to the iframe on the same domain, and the guest iframe can easily communicate with the host window as I originally suggested :

Host listens to the message event

window.addEventListener("message", myMessageHandler);

Guest iframe notifies the host.

window.top.postMessage({authenticationResult: "whatever_value"}, 'http://example.com');
M. Gallant
  • 236
  • 2
  • 5
  • Sorry, no, I should have been clearer regarding the embedding - all the website owner copies is a ‘script’ tag which references mysite.com and injects some HTML directly into the page. – b4tch Aug 02 '22 at 22:05
0

POPUPS AND IFRAME REDIRECTS

I would have a look at the oidc-client library, which used to do similar things. See the code in PopupWindow and the way that a named window object is used before the redirect and then used on the response. The OAuth state parameter was used to correlate the request and response:

// Before navigating
 window["popupCallback_" + params.id] = this._callback.bind(this);

// Notify opener upon return
var name = "popupCallback_" + data.state;
var callback = window.opener[name];
callback(url, keepOpen);

The library also performed some interesting iframe navigation if you look at the IFrameWindow and IFrameNavigator classes. Using iframes can be permissioned with your own authorization server, but will not work with Google or Twitter due to clickjacking protections.

BROWSER RESTRICTIONS

One of the reasons why the above library is inactive is browser restrictions on content from third party sites, which impacts some of the browser OpenID Connect behaviour.

Your widgets will be treated in a hostile manner by browsers, who will apply the same restrictions as they do on third party ads that try to track users across sites.

In particular, if your widget gets data by calling mysite.com with an HTTP-only cookie, this will be considered a third party cookie and dropped by the browser. Access tokens can work, but they are not considered the browser current best security practice.

MAIN WINDOW REDIRECTS

If you can't get popups to work, consider redirecting on the main window, which will work best from a browser restrictions viewpoint. A main window redirect serves as a user gesture so that any login cookies from Google etc are not dropped. Your flow might work like this, though it requires a design based on access tokens:

  • Customer page loads at example.com
  • Widget loads in an iframe at mysite.com and renders content that prompts the user to click a button to authenticate
  • When the button is clicked, the widget saves the parent URL in session storage, then redirects the parent window to Google, with a mysite com redirect URI
  • Main window at mysite.com conpletes the login, saves an access token to session storage, then redirects back to the stored parent URL
  • Customer page loads again at example.com
  • Widget loads again in an iframe at mysite.com and this time can get an access token from session storage to use for data access

From a user experience viewpoint, the user signs in to the main app initially, then once more to the widget as a separate provider. The impact on the host app is a bit like refreshing the page, which it will already cope with.

FUTURE

The FedCM initiative is aiming to solve this type of cross domain browser identity problem in a future facing way. It will not be ready any time soon, but it is worth reading their docs to identify potential issues with your own solution.

Gary Archer
  • 22,534
  • 2
  • 12
  • 24
  • Thanks for your answer. I didn’t get chance to test it before the bounty expired, but my gut feeling is that in Twitter’s case at least this won’t work due to the fact I had previously tested opening their oAuth process in an iframe and it failed due to their headers preventing embedding within an iframe. – b4tch Aug 08 '22 at 21:35
  • 1
    Ah, yes - a third party will want to prevent clickjacking, and I was forgetting that the oidc solution worked for owned domains. I will update my answer a little later. – Gary Archer Aug 09 '22 at 05:20
0

I couldn’t find a “proper” way to do this, so what I ended up doing was generating a unique code and passing that to the new window. This code gets stored in my database and updated with the ID of the user upon completion of the oAuth process. The main window uses setInterval to check for the existence of a user ID against the unique code every couple of seconds.

b4tch
  • 959
  • 9
  • 16