WebView: Solve the deprecated Facebook / Google Sign In in iOSWebView

Image by Jan from Pixabay

I have been asked at multiples occasion from my professional networks or business owners how quickly it is possible to “convert” a website to an App. Well, if we think quickly and naively it can be done in “minutes” however there is challenges that we do not expect if we are not familiar with that can add additional days..

In the fast-paced world of app development, leveraging hybrid solutions has become a go-to strategy for companies that already have a responsive website and are looking to make a swift entry into the mobile market. It is easy to promote to their customers but also to benefits from the sensible referencing push and marketing effect that App Stores gives. Often, these companies have web engineers but lack mobile developers, therefore they want something that they can control and maintain mostly.

The Hybrid Challenge

WebViews can come with its own set of challenges (target=”_blank” handling / pdf documents to download or links we want to present in an another page, etc).

All of this are still manageable as WeView delegates are quite good to handle this logic. But one of the most daunting is when it comes to integrating social logins from Google and Facebook.

Bridging the Gap

A few years ago, for security reasons, Google and Facebook are blocking web login from a WebView.

While in the past there were some hacky (and unstable) ways to go over it, there is a way to overcome it in a proper, secure and maintenable way. Since iOS 8.0+ and Android API 17+ we can leverage the Javascript Interface to communicate between Web and WebView. They have been improved lately to enhance the capabilities.

Many articles are already explaining communication between web and webview, so I will not go into so much details into that, but more specifically towards leveraging this feature to bridge the gap between WebView and Web for Social Login.

Crafting the Integration

For the sake of not transforming this article in a novel, I will focus on the iOS Implementation and I will reference few part of the solution for Android. It is pretty similar.

Step 1: Configuring WebView for Native Integration

First we need to setup a message handler that we will call nativeHandler that will be used to communicate between Web and WebView.

iOS 🍎 :

webView.configuration.userContentController.add(self, name: "nativeHandler")

Android 🤖 :

webView.addJavascriptInterface(new AndroidInterface(), "AndroidInterface");

class AndroidInterface {
    @JavascriptInterface
    public void sendMessageToNative(String message) {
        // Handle the message received from the web page
        // Replace this with your Android message handling code
        Log.d("Web_Message",message);
    }
}

Then, last step, set up a Javascript method that will be able to send a message from your Web front to the Android app or iOS app.

function sendMessageToNative(message) {
        if (isiOS) {
            // iOS WKWebView message handler "nativeHandler"
            window.webkit.messageHandlers.nativeHandler.postMessage(message);
        } else {
            // Android
            window.AndroidInterface.sendMessageToNative(message);
        }
    }

To listen any message coming throught on iOS, you can use the WKScriptMessageHandler delegate as per below:

extension WebViewController: WKScriptMessageHandler {
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        // Handle the received message from the web page
        if message.name == "nativeHandler" {
            if let messageBody = message.body as? String {
                print("Received message from web page: \(messageBody)")
            }
        }
    }
}

On Android just use the class AndroidInterface class refered in the code snipped at Step 1 at the start of this article.

That’s it ! You are all set to communicate between WebView and Web.

Now, you are free to decide what message and data structure you choose to send in the sendMessageToNative method.

A simple example for us would be:

{"message_type": "login_{google/facebook}"

Integrating Google SDK / Facebook SDK:

Once you receive the message on the App side, you can call the Native Google SDK or Facebook SDK Login to Login your User once you receive the message name you are listening at Step 1:
Integrating Google SDK / Facebook SDK:

Once you receive the message on the App side, you can call the Native Google SDK or Facebook SDK Login to Login your User once you receive the message name you are listening at Step 1:

if message.name == "nativeHandler" {
            if let messageBody = message.body as? String {
                // PARSE THE JSON STRING TO AN OBJECT
                // IF login_google -> googleSDK.signin()
                // IF login_facebook -> FacebookSDK.signin()
         }
  }


Step 2: Token Handoff

Once you get the OAuth token the respective SDK callback or delegate method. It just requires you to transmit it to the your web server in a secure manner. Your server should always be responsible for validating the token.

Now I am sure you would tell me, can we send it back to the web via a Javascript call from the App to the Web ?

Well, I would not recommend it. First, using it is not really maintenable and testable, secondly it create a lot of security concerns. If your website is also using Apple Pay it will be blocked by calling evaluateJavascript (unless top window is reloaded).

What I always advise is to create a tiny POST Web Services that will be able to receive the OAuth Token along side the loginType and any cookies header that the web server might need in order to update the according Session on their end.

Once the server responds, if any session changes it will be saved in the HTTPCookieStorage. We can just sync the cookie storage with the WebView cookies storage as per below:

func syncCookies() {
   let cookies = HTTPCookieStorage.shared.cookies ?? []
   for cookie in cookies {
   configuration.websiteDataStore.httpCookieStore.setCookie(cookie)
   }
}

Then simply reloading the webview page should show the user connected (if your server did the right job :)) ! 🎉 🎉.

Conclusion

In summary, navigating the challenges of social logins within WebView involves configuring the message handler, crafting the integration with care, and securely handling token handoff. Now, let’s wrap up this guide by emphasizing the significance of this strategic bridge into the app market.

Having walked the path of integrating social logins into WebView, I share this battle-tested guide with fellow App developers. For companies with web engineers but no mobile developers, this solution serves as a strategic bridge into the app market. Embrace the Javascript Bridge and navigate the nuances, ensuring a secure and seamless integration.

Final Thoughts

To sum up, as an native mobile app developer, adaptability and collaboration are your allies.

While I always prefer doing an App natively and I am personally not in favor of so much embedded web app, I can understand from a busines standpoint the initial foot step in that water. Usually when the first version is on the app store, the path is towards a fully integrated native App which requires more time and effort but does not block business to start promoting and leveraging the App benefits.

Lastly, other challenges remains for specifics features, such as User-Targeted Push Notifications but this will require an another post :).

Thanks for reading it ! Do not hestitate to comment out !

WebView: Solve the deprecated Facebook / Google Sign In in iOSWebView
Scroll to top