18

I've been working on an app using React and Next.js, currently adding PWA support.

Users log in to the app via the Google OAuth flow. I was originally using the JS client which utilizes a pop-up window, but that ran into errors in the PWA. I'm now using the normal OAuth flow by redirecting the user to Google's OAuth URL.

This works fine in the browser. In the standalone PWA on iOS, it opens the OAuth page in a new Safari window. This means that the OAuth flow is carried out in Safari, and at the end the user is left using the app in Safari rather than the standalone PWA.

I'm redirecting using this method:

export function setHref(newLocation: string) {
  window.location.href = newLocation;
}

This even looks to be the method everyone recommends to avoid pop-ups when redirecting in your PWA. Has this changed recently? Or is there another method to carry out redirects/OAuth flows inside a standalone progressive web app?

Jakemmarsh
  • 4,601
  • 12
  • 43
  • 70

3 Answers3

19

I have a workaround that solve the oauth redirection problem on ios safari standalone web app.

The problem is the manifest meta tag, it seems that webkit (safari) implemented it with an old specification (Chromium had the same problem and fix it in a recent version).

I based the workaround by modifying Google´s PWACompat Javascript you can get on:

https://github.com/GoogleChromeLabs/pwacompat/blob/master/pwacompat.js

PWAcompat js is useful to generate the proper html meta tags, in order to have an standalone web app with home icons and an splash screen

You need to do a small "hack" on PwaCompat script and in your "manifest" meta tag by replacing the name of the meta tag by any identifier, for example, in your index.html:

<link rel="pwa-setup" href="manifest.json" >
<script async src="js/pwacompat.js"></script>

manifest.json contains your standard manifest.json declaration, with the name, icons and styling for your web app.

js/pwacompat.js, contains a copy of pwacompat.js from google, with this small modification ( line 36) :

Change :

const manifestEl = document.head.querySelector('link[rel="manifest"]');

by

const manifestEl = document.head.querySelector('link[rel="pwa-setup"]');

where pwa-setup is the name you place on meta tag, and that´s it, you have your manifest.json interpreted and oauth redirection in the same standalone context

UPDATE: From IOS 13 and above this workaround is not longer necessary. Anyway, if you want to keep the compatibility with IOS < 13, you can use the following script to check the IOS Version and condition the workaround use:

<script>

        var iOS = (/iP(hone|od|ad)/.test(navigator.userAgent));
        if (iOS) {
            var v = (navigator.appVersion).match(/OS (\d+)_(\d+)_?(\d+)?/);
            var iOSversion = parseInt(v[1], 10);
            console.log(iOSversion);
            if(iOSversion < 13) {
                document.querySelector('link[rel="manifest"]').setAttribute("rel", "no-on-ios");


            }
        }
</script>
Jorge Valvert
  • 927
  • 1
  • 11
  • 20
  • What should I set as `display` in my `manifest.json`? `standalone` (what I expected) didn't work for me with Firebase Auth (OAuth) - it redirects to Safari, but doesn't redirect back, and doesn't remember auth cookies.. – Kirill Groshkov Sep 22 '18 at 16:38
  • Yes, you have to set display :standalone. Did you change manifest meta tag? Did you change oauth method to “redirect” when you create the oauth client? by default oauth2 client is in popup mode. – Jorge Valvert Sep 23 '18 at 17:54
  • I set `standalone` I changed manifest and pwa.js script, according to instruction. Redirect method... I've checked and, actually, you're right, I didn't change it, it was still set to `signInWithPopup`. So next time I should try with redirect to see if it changes anything. But even with `popup` as a method it still did "redirect", it opened a new safari window. So, I'll try again... – Kirill Groshkov Sep 25 '18 at 13:28
  • @JorgeValvert Mate, you have saved my project from failure because of that answer! Thanks a ton! – fxdxpz Oct 01 '18 at 09:33
  • This did indeed work, thanks so much! Can you explain why we have to change the `rel` tag and selector? – Jakemmarsh Oct 18 '18 at 21:19
  • One thing I'm noticing is that changing the `rel` to anything but `"manifest"` makes Google Chrome think there's no manifest at all. – Jakemmarsh Oct 18 '18 at 23:03
  • 1
    Jakemmarsh, since this is a workaround intended to fix the wrong safari´s behaviour on standalone web apps ( Chromium had the same problem but google fix it several releases ago), Indeed chrome is not able to parse manifest.json, but no worries, pwcompat does the job. Until Apple releases a bug fix for it, we have to survive with this workaround ( I reported the bug following the apple guidelines, and I´m still waiting for the release that includes a fix for the problem) – Jorge Valvert Oct 18 '18 at 23:29
  • 1
    Great job! https://github.com/firebase/firebase-js-sdk/issues/77#issuecomment-432413393 – jvitor83 Oct 23 '18 at 20:49
  • This works for me but without a manifest, all internal links in my PWA open in a browser tab. How did you solve this issue? – Thomas Oct 24 '18 at 07:47
  • The manifest is not a dependency for the service worker, It works very well without the manifest.json. I tested the service worker on ios 12 and android 8 and it performs well on api cache and static precache (I recomend workbox to simplify your service worker deployment) – Jorge Valvert Oct 26 '18 at 17:40
7

The good solution for now is to hack it through pwacompat. But on Android changing the manifest rel attribute to "pwa-setup" does not comply with webapp requirements thus the install to home popup doesn't appear.

<link rel="pwa-setup" href="manifest.json" >
<script async src="js/pwacompat.js"></script>

Changed line #36

const manifestEl = document.head.querySelector('link[rel="pwa-setup"]');

A better solution is to identify if the webapp is rendered on ios or android, then change the rel attribute in "runtime"

<link rel="manifest" href="manifest.json">
<link rel="pwa-setup" href="manifest.json">
<script src="pwacompat.js"></script>
<script>
   var iOS = !!navigator.platform && /iPhone|iPod/.test(navigator.platform);
   if(iOS) {
      document.querySelector('link[rel="manifest"]').setAttribute("rel", "no-on-ios");
   }
</script>
Lester
  • 283
  • 7
  • 14
1

I combined @Lester answer with @Roysh and a few more to make it more PWA replaceable on iOS. Since it lost manifest it will use title as default name and now open the current path instead of start_url from manifest.

<link rel="manifest" href="manifest.webmanifest">
<script>
  if (!!navigator.platform && /iP(?:hone|ad|od)/.test(navigator.platform)) {
    document.querySelector(`link[rel="manifest"]`).setAttribute(`rel`, `no-ios`);
    document.title = `AppName`; // default app name | simulate short_name
    if (`standalone` in window.navigator && window.navigator.standalone && sessionStorage.getItem(`iOS-redirect`) === null) {
      sessionStorage.setItem(`iOS-redirect`, ``);
      window.location = `/`; // simulate start_url
    }
  }
</script>
hisoft
  • 763
  • 5
  • 8
  • 1
    I read through all the threads, copy and pasted this, worked perfectly! Thanks! One slight thing, I changed href to use create-react-apps default - href="%PUBLIC_URL%/manifest.json" – gamengineers Jan 11 '19 at 17:30
  • I'd be more than grateful if someone could quickly repeat what has worked for him/her to allow using the google/fb-login with redirect in a iOS standalone web app saved to the home screen. No matter what solution I try, the PWA gets left when logging in and is not returned to. – alexeis Jan 16 '19 at 12:41