Closed Bug 1371067 Opened 7 years ago Closed 4 years ago

PushManager subscribe() automatically failing with NotAllowedError when permission state is prompt

Categories

(Core :: DOM: Push Subscriptions, defect, P3)

53 Branch
defect

Tracking

()

RESOLVED WORKSFORME

People

(Reporter: bhhronik, Unassigned)

Details

User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36

Steps to reproduce:

Within service worker code I have the following snippet:

```  self.registration.pushManager.permissionState()
    .then(function(state) {
      if (state === 'prompt') {
        self.registration.pushManager.subscribe({
          userVisibleOnly: true,
          applicationServerKey: applicationServerKey
        })
          .then(function(newsubscription) {
            console.log("sending new subscription");
            // ... send subscription info to server
          })
          .catch(function(err) {
            console.warn('Failed to subscribe the user: ', err);
          });
      }
      console.log(state);
    });```

Firefox version: 53.03
OS Version: 10.11.6


Actual results:

self.registration.pushManager.permissionState() consistently returns 'prompt', so the call to self.registration.pushManager.subscribe(...) should prompt the user to enable notifications. However, it automatically errors with "NotAllowedError: User denied permission to use the Push API."


Expected results:

I would expect the above error to occur only if the permission state was 'denied', however for a permission state of 'prompt' I would expect the prompt to occur asking a user if they would like to allow or disallow notifications.
Component: Untriaged → DOM: Push Notifications
Product: Firefox → Core
Confirmed that same behavior exists on the newest nightly build as well as the newest beta release.
To support my expectation, from steps 8 and 9 outlining the PushManger's subscribe method, taken from the w3c Editor's draft(https://w3c.github.io/push-api/#dom-pushmanager):

"""
8.Ask the user whether they allow the webapp to receive push messages, unless a prearranged trust relationship applies or the user has already granted or denied permission explicitly for this webapp.

9. If not granted, reject promise with a DOMException whose name is "NotAllowedError" and terminate these steps.
"""

Unless I'm mistaken, the permission state of 'prompt' should signal that user has not already granted or denied permission explicitly for the webapp.
Kit probably knows what should be happening here.
Flags: needinfo?(kit)
Hi Brent, thanks for the report! I suspect this will end up as a WONTFIX. It's not possible to ask for permission from within the service worker: permission doorhangers are always associated with a window, but there might not be any controlled windows open when the worker runs...and showing a disembodied doorhanger would make for a confusing UX.

Could I ask why you're checking the permission in the service worker? Is this in a `pushsubscriptionchange` event handler, or somewhere different? (IIRC, if the user revokes the permission, we won't fire `pushsubscriptionchange` until the permission is granted again, because the worker can't resubscribe). Given that you can't prompt, I think you could remove the check and let the `subscribe` call fail, but I'm missing the full context of where that snippet is called.
Flags: needinfo?(kit) → needinfo?(bhhronik)
Hi Kit, thanks for the response!

The reason why I'm asking for permission from within the ServiceWorker is so that I can ask for permission in response to a message(which could be sent from multiple locations, settings page, banner popups, etc) being sent to the ServiceWorker. A more complete snippet on the service worker side would look something like(more going on certainly, but sharing the parts that convey the idea):

```
self.addEventListener('message', function(event){
  var message = JSON.parse(event.data);
  if ("enable" in message) {
    console.log("service worker enabling notification");
    enableBrowserNotifications();
  }
});

function enableBrowserNotifications() {
  self.registration.pushManager.permissionState()
    .then(function(state) {
      if (state === 'prompt') {
        self.registration.pushManager.subscribe({
          userVisibleOnly: true,
          applicationServerKey: applicationServerKey
        })
          .then(function(newsubscription) {
            console.log("sending new subscription");
            // ... send subscription info to server
          })
          .catch(function(err) {
            console.warn('Failed to subscribe the user: ', err);
          });
      }
      console.log(state);
    });
}
```
Flags: needinfo?(bhhronik)
Priority: -- → P3
Hey Kit,

Any update on this? It isn't immediately obvious to me how we would be able to create a subscription in response to a message sent to a ServiceWorker without being able to create a subscription from the ServiceWorker. 

Thanks!
(In reply to Brent from comment #6)
> Any update on this? It isn't immediately obvious to me how we would be able
> to create a subscription in response to a message sent to a ServiceWorker
> without being able to create a subscription from the ServiceWorker.

Sadly, I don't think this will work. Since the worker can run in the background, when none of the site's windows are open, there's nowhere that we can show the permission doorhanger. You'll need to call `pushManager.subscribe` when you call `navigator.serviceWorker.register`.

Martin, should we add a non-normative note to the spec that workers can't ask for permission? Or is this supposed to work?

FWIW, I tried this in Chrome, and saw the same behavior. Calling `registration.pushManager.subscribe` from the main page shows the doorhanger. OTOH, registering a service worker, sending a message via `postMessage`, and calling `self.registration.pushManager.subscribe` from the worker's `onmessage` handler rejects with "DOMException: Registration failed - permission denied". Are you seeing different results in Chrome, Brent?
Flags: needinfo?(martin.thomson)
Hi Kit,

Thanks for the response! Yeah, I was able to get around by doing something like this(instead of sending the message to the push subscription):
```
      navigator.serviceWorker.ready.then(function (registration) {
        const applicationServerKey = urlBase64ToUint8Array(applicationServerPublicKey);

        registration.pushManager.getSubscription().then(function (subscription) {
          // Do stuff
          
        });
      });
```

I'm actually running into issues on Chrome on the sending side(receiving 400 instead of 201 which I'm receiving from Firefox). Would you happen to know if Chrome and Firefox handle VAPID(conforming to the v02 spec) in subtly different ways?
Kit, https://github.com/w3c/push-api/issues/260
Flags: needinfo?(martin.thomson)

:annevk, am I interpreting correctly that https://github.com/w3c/push-api/commit/d0b78c3b1266f5d045ab5cebf1fe7e454821f2fc suggests now a WORKSFORME ?

Flags: needinfo?(annevk)

Yeah, though I filed https://github.com/w3c/push-api/issues/322 to turn that into a normative requirement and get the specific exception defined as well. If that's not what we do here we might need a follow-up bug to change the exception.

Status: UNCONFIRMED → RESOLVED
Closed: 4 years ago
Flags: needinfo?(annevk)
Resolution: --- → WORKSFORME
You need to log in before you can comment on or make changes to this bug.