App icons on a phone screen
February 4, 2019

Facebook Login from a Standalone PWA

Random Thoughts

I'm a computer nerd. Formerly a professional one. I still use my programming skills in my magic. I wrote my show control software, and do several routines that involve custom programming. Occasionally I encounter technical problems problems where my Google skills don't turn up a clear solution. If I do find a solution, I'll try to document it for future Googlers to find. This is one of those posts.

So, if you're trying to integrate Facebook login into a PWA running in standalone mode, read on! Everyone else will probably want to turn away now.

Facebook login works great from websites. But try to run a progressive web app in standalone mode, and suddenly, it's not so simple. The login popup goes blank, and nothing happens. My searching for a solution turned up a few breadcrumbs, but no complete working solution. I did get it working, though.

Apparently there are conversations about changing the way standalone PWA's work to simplify OATH. But for now, here's what worked for me.

In short, I set up a custom login flow with Facebook. It created a login popup that opened Facebook's OATH link which, in turn, forward to a special page I set up. That page in the popup window communicated with the standalone window through Window.postMessage() to pass the token back, so the standalone window could make API calls. The standalone then closed the popup.

Here is the longer version:

My app used Window.open() to create a popup window calling Facebook's OATH URL. You may have different needs, but my call looks like this (link here has been anonymized and breaks added for readability):

https://www.facebook.com/v3.2/dialog/oauth
?client_id=9999999999
&display=popup
&redirect_uri=https://example.com/login.htm
&state=aaaaaaaaaa
&auth_type=rerequest
&response_type=token
&scope=manage_pages,publish_pages

See Facebook's documentation for setting up a custom login flow for more details.

After Facebook completes the login process, it forwards to your "redirect_url" in your popup window. The relevant data, including the token will be in the URL's fragment (the part following the #). That page will need to pass that information back to your main window. Below is the script I used on that page. It just converts the fragment to JSON and passes it back for processing.

// Remove hash and spit into array
// of "XXX=YYY" values
let fragmentParts =
        window.location.hash.slice(1).split("&");
let myJson = {};

// Convert hash elements to JSON
// E.g. XXX=YYY becomes myJSON.XXX="YYY"
fragmentParts.forEach(function (part) {
    let parts = part.split("=");
    myJson[parts[0]] =
        decodeURIComponent(parts[1]);
});

// Send JSON data to main window
window.opener.postMessage(myJson,
        "https://example.com");

The standalone window has a listener set up. When it gets the message from the login window, it closes the popup.

The standalone page also has a timer that regularly checks if the login page was closed before it sent back a message.

The main window now has the token it needs to make Facebook API calls. The problem is getting the Facebook SDK to use it. Normally, the SDK retrieves the token, stores it in a cookie, and manages it for you. If you don't use the standard login method from the SDK, this won't happen.

You'll need to store the token somewhere and pass the token manually on every API call. For example:

FB.api("/me/accounts",
        {
            access_token: "access token goes here",
            other_fields_as_appropriate: "??????????"
        },
        function (response) {
    //process the return from the api call
});

As I didn't use the FB.login, I couldn't use FB.logout. So, to log out I just deleted the copy of the token, effectively ending the session.

I don't know if this is the best solution, but it worked for me. Hopefully it will help someone else.

Leave a Reply

Your email address will not be published. Required fields are marked *

Please fill out this field

Please enter a valid email address

Please fill out this field

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Cancel Reply