Getting Browser User Permission with the Permissions API

John Au-Yeung - Jan 22 '21 - - Dev Community

Check out my books on Amazon at https://www.amazon.com/John-Au-Yeung/e/B08FT5NT62

Subscribe to my email list now at http://jauyeung.net/subscribe/

We have to get user consent when we want to do anything that changes the user experience. For example, we need to get user permission to show notifications or access their hardware.

Modern browsers are standardized in how permissions are obtained. It’s all in the Permissions API.

APIs that rely on the Permissions API for permissions include the Clipboard API, Notifications API, Push API, and Web MIDI API.

The navigator object has the permissions property in both the standard browser and worker contexts. It gives us a way to get the permissions. However, the way we request permissions is different for each API.

This API is experimental so check if the browsers you target are supported.

Querying Permissions

The navigation.permissions object lets us query permissions set with the query method.

It returns a promise which gets us the state of the permission. For example, we can write the following to check for permission for the geolocation API:

(async () => {
  const result = await navigator.permissions.query({
    name: 'geolocation'
  });
  console.log(result.state);
})();
Enter fullscreen mode Exit fullscreen mode

The possible values for result.state are 'granted', 'denied', or 'prompt'.

There’s also one for Web Workers that do the same thing.

The object that we passed into the query method is the permission descriptor object, which has the following properties:

  • name — name of the API which we want to get the permission for. Firefox only supports geolocation, notifications, push, and persistent-storage
  • userVisibleOnly — indicates whether we want to show notification for every message or be able to send silent push notifications. Default value is false .
  • sysex — applies to MIDI only. Indicates whether we need to receive system exclusive messages. Default value is false .

Requesting Permission

Requesting permission is different between APIs. For example, the Geolocation API will show a prompt for permission when we call getCurrentPosition() as follows:

navigator.geolocation.getCurrentPosition((position) => {
  console.log('Geolocation permissions granted');
  console.log(`Latitude: ${position.coords.latitude}`);
  console.log(`Longitude: ${position.coords.longitude}`);
});
Enter fullscreen mode Exit fullscreen mode

The Notifications API has a requestPermission method to request permissions:

Notification.requestPermission((result) => {
  console.log(result);
});
Enter fullscreen mode Exit fullscreen mode

Watching for Permission Changes

The navigator.permissions.query promise resolves to a PermissionStatus object. We can use it to set an onchange event handler function to watch for permission changes.

For example, we can expand on this example to add an event handler function to watch for changes for the permission of the Geolocation API:

(async () => {
  const result = await navigator.permissions.query({
    name: 'geolocation'
  });
  console.log(result.state);
})();
Enter fullscreen mode Exit fullscreen mode

We can write:

(async () => {
  const permissionStatus = await navigator.permissions.query({
    name: 'geolocation'
  });
  permissionStatus.onchange = () => {
    console.log(permissionStatus.state);
  }
})();
Enter fullscreen mode Exit fullscreen mode

The permissionStatus.state will have the permission for the Geolocation API.

It’ll log whenever the permission changes, such as when we change the permission setting in the browser.

Revoking Permissions

We can revoke permission by using the revoke method.

To call it, we can access it from the navigator.permissions object as follows:

navigator.permissions.revoke(descriptor);
Enter fullscreen mode Exit fullscreen mode

Where descriptor is the permission descriptor which we described above.

It returns a Promise that resolves with the permission status object indicating the result of the request.

This method throws a TypeError if getting the permission descriptor failed or the permission doesn’t exist or is unsupported by browsers.

The name property of the permission descriptor can accept one of the following values 'geolocation', 'midi', 'notifications', and 'push'.

Other properties remain the same as the query method.

For example, we can use it by writing:

(async () => {
  const permissionStatus = await navigator.permissions.revoke({
    name: 'geolocation'
  });
  console.log(permissionStatus);
})();
Enter fullscreen mode Exit fullscreen mode

Note that we’ve to set dom.permissions.revoke.enable to true in Firefox 51 or later to use this method. It’s not enabled in Chrome.

The Permissions API lets us check and revoke browser permissions in a uniform way.

Requesting permissions are different for each API. This isn’t changing any time soon.

This is an experimental API, so using it in production is probably premature.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .