The Permissions API allows a web application to be aware of the status of a given permission, to know whether it is granted, denied or if the user will be asked whether the permission should be granted.

This specification defines conformance criteria that apply to a single product: the user agent that implements the interfaces that it contains.

Implementations that use ECMAScript to expose the APIs defined in this specification MUST implement them in a manner consistent with the ECMAScript Bindings defined in the Web IDL specification [[!WEBIDL]].

Dependencies

The following concepts and interfaces are defined in [[!HTML]]:

Promise objects are defined in [[!ECMASCRIPT]].

Scope of this document

This document's goal is to specify an API that will help developers to handle permissions on the Web platform. Web APIs have different ways to deal with permissions. The [[notifications]] API allows developers to request a permission and check the permission status explicitly. Others might only expose the status to web pages when they try to use the API, like the [[geolocation-API]] which fails if the permission was not granted without allowing the developer to check beforehand.

Being able to know whether an API call is going to prompt is useful in order to provide a good user experience. Unfortunately, more often than not, those prompts can't be controlled by developers.

The API specified in this document is meant to provide the tools so that web applications can improve their user experience when permissions are involved.

The solution described in this document is meant to be extensible but isn't meant to be applicable to all the current and future permissions available in the web platform. If you are working on a specification that has a permission model that wouldn't fit in the model described in this document, please contact the editors or file an issue. We would love to hear about it.

The initial intent of this document was to allow web applications to request and revoke permissions explicitly in addition to querying the permission status. This is an aspect of the specification that was controversial thus removed from the current document in a spirit of incremental changes: settling on a small API that can be improved.

Privacy considerations

Permission states can be used as an element of fingerprinting by websites. Usually websites could already have access to the information but often through actually using the API which could lead to a permission request UI if the permission was not already granted. Thus, even though this API doesn't expose new fingerprinting data to websites, it makes it easier for them to have discreet access to it. Therefore, implementations are encouraged to have an option for users to block (globally or selectively) the querying of permission states.

Permission descriptor

required PermissionName name

A permission is defined by a name and other properties that depend on the name. The simplest permissions require only a name, but some others will require more information, in which case, they should have an associated PermissionDescriptor dictionary that inherits from PermissionDescriptor.

Permission Registry

geolocation
notifications
push
midi
camera
microphone
speaker
device-info

The PermissionName enum defines the list of known permission names.

Geolocation

The geolocation permission is the permission associated with the usage of the [[geolocation-API]].

Notifications

The notifications permission is the permission associated with the usage of the [[notifications]] API.

Push

The push permission is the permission associated with the usage of the [[push-api]]. It has an associated PermissionDescriptor, PushPermissionDescriptor:

boolean userVisibleOnly = false

Midi

The midi permission is the permission associated with the usage of [[webmidi]]. It has an associated PermissionDescriptor, MidiPermissionDescriptor:

boolean sysex = false

Media Devices

The camera, microphone , and speaker permissions are associated with permission to use media devices as specified in [[GETUSERMEDIA]] and [[audio-output]].

These three permissions have an associated PermissionDescriptor:

DOMString deviceId

A permission covers access to the device given in the associated descriptor.

If the descriptor does not have a deviceId, its semantic is that it queries for access to all devices of that class. Thus, if a query for the "camera" permission with no deviceId returns "granted", the client knows that there will never be a permission prompt for a camera, and if "denied" is returned, it knows that no getUserMedia request for a camera will succeed.

If a permission state is present for access to some, but not all, cameras, a query without the deviceId will return "prompt".

The device-info permission controls access to names and capabilities of input and output devices.

A successful call to the getUserMedia function of [[GETUSERMEDIA]] MUST cause permission to be granted for the returned devices, and MAY cause other permissions to be granted.

Stopping a MediaStreamTrack MAY cause permission to be revoked for the associated device.

Permission Store

User agents MAY use a form of storage to keep track of web site permissions. When they do, they MUST have a permission storage identifier which is linked to a permission storage entry. The permission storage identifier MUST contain the website's origin and MAY contain other information like the embedding status or the embedder's origin. The permission storage entry MUST be a PermissionState or undefined.

The steps to retrieve a permission storage entry of a permission storage identifier are as follows:

  1. If the user agent has a permission storage entry associated with the permission storage identifier in its permission store, it MUST return the permission storage entry.
  2. Otherwise, it MUST return undefined.

The steps to create a permission storage entry for a permission storage identifier are as follows:

  1. If the user agent has a permission storage entry associated with the permission storage identifier in its permission store, it MUST overwrite it to the given permission storage entry.
  2. Otherwise, it MUST write the new permission storage entry to its permission store.

The steps to delete a permission storage entry of a permission storage identifier are as follows:

  1. If the user agent has a permission storage entry associated with the permission storage identifier in its permission store, it MUST remove it.

Status of a permission

granted
denied
prompt

The granted state represents that the caller will be able to successfuly access the feature without having the user agent asking the user's permission.

The denied state represents that the caller will not be able to access the feature.

The prompt state represents that the user agent will be asking the user's permission if the caller tries to access the feature. The user might grant, deny or dismiss the request.

The steps to retrieve the permission state of an origin and global object for a given permission are as follows:

  1. Let identifier be the permission storage identifier associated with the origin, global object and permission.
  2. Run the steps to retrieve a permission storage entry.
  3. If the result of those steps are not undefined, return it and abort these steps.
  4. Otherwise, the user agent MUST return a default value based on user agent's defined heuristics. For example, prompt can be a default value but it can also be based on frequency of visits.
readonly attribute PermissionState state
attribute EventHandler onchange

A PermissionStatus instance has an associated permission that is a PermissionDescriptor.

The steps to update the state of a PermissionStatus instance are as follows:

  1. Let status be the PermissionStatus instance being updated.
  2. Run the steps to retrieve the permission state using the Document/Worker's origin, status' global object and status' associated permission then set the result of those steps to the state attribute.

The steps to create a PermissionStatus for a given permission are as follow:

  1. Let status be a PermissionStatus instance.
  2. Set the given permission parameter as the status' associated permission.
  3. Run the steps to update the state on status.
  4. Return status.

The state attribute MUST return the latest value that was set while running the update the state steps on the current instance.

The onchange attribute is an event handler whose corresponding event handler event type is change.

Whenever the user agent is aware that the state of a PermissionStatus instance has changed, it MUST asynchronously run the following steps:

  1. Let status be the PermissionStatus for which the state has changed.
  2. Run the steps to update the state of status.
  3. Queue a task on the permission task source to fire a simple event named change at status.

Navigator and WorkerNavigator extension

A Permissions instance is exposed on the navigator object for Window and Worker contexts.

readonly attribute Permissions permissions;
readonly attribute Permissions permissions;

Permissions interface

        [Exposed=(Window,Worker)]
        interface Permissions {
          Promise<PermissionStatus> query(PermissionDescriptor permission);

          Promise<PermissionStatus> request((PermissionDescriptor or sequence<PermissionDescriptor>) permissions);

          Promise<PermissionStatus> revoke(PermissionDescriptor permission);
        };
      

When the query() method is invoked, the user agent MUST run the following steps:

  1. If permission.name has an associated PermissionDescriptor, convert the underlying ECMAScript object to the associated PermissionDescriptor dictionary as described in [[!WEBIDL]], then:
    • If that operation failed, return a Promise rejected with a TypeError and abort these steps.
    • Otherwise, set permission to the result of the operation.
  2. Let promise be a newly-created Promise.
  3. Return promise and continue the following steps asynchronously.
  4. Run the steps to create a PermissionStatus using permission and resolve promise with the result of those steps.
If a developer wants to check multiple permissions at once, the editors recommend the use of Promise.all(). An example can be found in the Examples section.

When the request() method is invoked, the user agent MUST run the following steps:

  1. TODO

When the revoke() method is invoked, the user agent MUST run the following steps:

  1. If permission.name has an associated PermissionDescriptor, convert the underlying ECMAScript object to the associated PermissionDescriptor dictionary as described in [[!WEBIDL]], then:
    • If that operation failed, return a Promise rejected with a TypeError and abort these steps.
    • Otherwise, set permission to the result of the operation.
  2. Let promise be a newly-created Promise.
  3. Return promise and continue the following steps asynchronously.
  4. Let identifier be the permission storage identifier associated with the origin, global object and permission.
  5. Run the steps to delete a permission storage entry using identifier.
  6. Run the steps to create a PermissionStatus using permission and resolve promise with the result of those steps.

Examples

This example uses the Permissions API to decide whether local news should be shown using the Geolocation API or with a button offering to add the feature.

<script>
  navigator.permissions.query({name:'geolocation'}).then(function(result) {
    if (result.state == 'granted') {
      showLocalNewsWithGeolocation();
    } else if (result.state == 'prompt') {
      showButtonToEnableLocalNews();
    }
    // Don't do anything if the permission was denied.
  });
</script>

This example is using the notifications permission for a chat application to show a notification button depending on the permission state.

<script>
  function updateNotificationButton(state) {
    document.getElementById('chat-notification-button').disabled = (state == 'denied');
  }

  navigator.permissions.query({name:'notifications'}).then(function(result) {
    updateNotificationButton(result.state);

    result.addEventListener('change', function() {
      updateNotificationButton(this.state);
    });
  });
</script>

This example is checking whether the page has the geolocation and the notifications permissions using Promise.all.

<script>
  Promise.all([navigator.permissions.query({name:'geolocation'}),
               navigator.permissions.query({name:'notifications'})])
  .then(function(result) {
    console.log('Geolocation permission state is ' + result[0].state);
    console.log('Notifications permission state is ' + result[1].state);
  });
</script>

This example is checking whether the page has the camera permission for the first device in the list. Error checking omitted.

<script>
  navigator.mediaDevices.enumerateDevices()
  .then(function(devices) {
     return navigator.permissions.query({name:'camera',
                                         deviceId: devices[0].deviceId})
  })
  .then(function(result) {
     console.log('Camera permission to first camera is ' + result.state);
  }
  .catch(err => console.log('Bad things happened: ' + err.name));
</script>

Acknowledgments

The editors would like to thank Adrienne Porter Felt, Anne van Kesteren, Domenic Denicola, Jake Archibald and Wendy Seltzer for their help with the API design and editorial work.