The Permissions API

Editor’s Draft,

This version:
https://w3c.github.io/permissions/
Feedback:
public-webappsec@w3.org with subject line “[permissions] … message topic …
Issue Tracking:
GitHub
Editors:
Mounir Lamouri (Google Inc.)
Marcos Cáceres (Mozilla)
Jeffrey Yasskin (Google Inc.)
Participate:
We are on Github.
File a bug.
Commit history.
Implementation status:
Blink/Chromium
Gecko

Abstract

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.

Status of this document

This is a public copy of the editors’ draft. It is provided for discussion only and may change at any moment. Its publication here does not imply endorsement of its contents by W3C. Don’t cite this document other than as work in progress.

Changes to this document may be tracked at https://github.com/w3c/webappsec.

The (archived) public mailing list public-webappsec@w3.org (see instructions) is preferred for discussion of this specification. When sending e-mail, please put the text “permissions” in the subject, preferably like this: “[permissions] …summary of comment…

This document was produced by the Web Application Security Working Group.

This document was produced by a group operating under the 5 February 2004 W3C Patent Policy. W3C maintains a public list of any patent disclosures made in connection with the deliverables of the group; that page also includes instructions for disclosing a patent. An individual who has actual knowledge of a patent which the individual believes contains Essential Claim(s) must disclose the information in accordance with section 6 of the W3C Patent Policy.

This document is governed by the 1 September 2015 W3C Process Document.

1. Scope of this document

This section is non-normative.

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.

2. Privacy considerations

This section is non-normative.

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.

3. Permission descriptor

dictionary PermissionDescriptor#dictdef-permissiondescriptorReferenced in:3. 
    Permission descriptor
  4. 
    Permission Registry
   (2)4.3. 
      Push
    4.4. 
      Midi
    4.5. 
      Media Devices
    6. 
    Status of a permission
   (2)8. 
    Permissions interface
   (2) (3) (4) (5) (6)9. 
    Common permission algorithms
   (2) {
  required PermissionName name#dom-permissiondescriptor-nameReferenced in:6. 
    Status of a permission
   (2);
};

A permission is described by a name and other properties that depend on the name. The simplest permissions require only a name, but some others have more detailed structure that requires more information to describe it. In that case, they should define a customized permission descriptor type dictionary that inherits from PermissionDescriptor.

4. Permission Registry

enum PermissionName#enumdef-permissionnameReferenced in:3. 
    Permission descriptor
  4. 
    Permission Registry
  5. 
    Permission Store
  6. 
    Status of a permission
   {
  "geolocation",
  "notifications",
  "push",
  "midi",
  "camera",
  "microphone",
  "speaker",
  "device-info",
  "background-sync",
};

Each enumeration value in the PermissionName enum identifies a permission#permissionReferenced in:4. Permission Registry (2)6. Status of a permission (2) (3)8. Permissions interface , which consists of the following algorithms and types:

A permission descriptor type#permission-descriptor-typeReferenced in:3. Permission descriptor 4. Permission Registry (2)4.3. Push 4.4. Midi 4.5. Media Devices 8. Permissions interface (2) (3) (4) (5) (6)
PermissionDescriptor or one of its subtypes. If unspecified, this defaults to PermissionDescriptor.
A permission storage type#permission-storage-typeReferenced in:4. Permission Registry (2) (3)4.5. Media Devices
PermissionStorage or one of its subtypes. If unspecified, this defaults to PermissionStorage.
A permission result type#permission-result-typeReferenced in:4. Permission Registry (2) (3) (4)4.5. Media Devices 6. Status of a permission
PermissionStatus or one of its subtypes. If unspecified, this defaults to PermissionStatus.
A permission query algorithm#permission-query-algorithmReferenced in:4.5. Media Devices 6. Status of a permission
Takes an instance of the permission descriptor type, an instance of the permission storage type that’s currently stored for this permission, and a new or existing instance of the permission result type, and updates the permission result type instance with the query result. Used by Permissions' query() method and the PermissionStatus update steps. If unspecified, this defaults to the boolean permission query algorithm.
A permission request algorithm#permission-request-algorithmReferenced in:4.5. Media Devices 8. Permissions interface
Takes the previously-stored instance of the permission storage type, an instance of the permission descriptor type, and a newly-created instance of the permission result type. Shows the user any necessary prompt to try to increase permissions, and updates the instances of the permission storage type and permission result type to match. May return a Promise if the request can fail exceptionally. (Merely being denied permission is not exceptional.) Used by Permissions' request() method, which handles reading and writing the permission store. If unspecified, this defaults to the boolean permission request algorithm.
A permission revocation algorithm#permission-revocation-algorithmReferenced in:4.5. Media Devices 8. Permissions interface
Takes no arguments. Updates any other parts of the implementation that need to be kept in sync after an entry is removed from the permission store. Triggered by Permissions' revoke() method. If unspecified, this defaults to doing nothing.

A boolean permission#boolean-permissionReferenced in:4.1. Geolocation 4.2. Notifications is a permission with all types and algorithms defaulted.

4.1. Geolocation

The "geolocation"#dom-permissionname-geolocationReferenced in:4. Permission Registry 10. Examples permission is the permission associated with the usage of the [geolocation-API]. It is a boolean permission.

4.2. Notifications

The "notifications"#dom-permissionname-notificationsReferenced in:4. Permission Registry 10. Examples (2) permission is the permission associated with the usage of the [notifications] API. It is a boolean permission.

4.3. Push

The "push"#dom-permissionname-pushReferenced in:4. Permission Registry permission is the permission associated with the usage of the [push-api].

permission descriptor type
dictionary PushPermissionDescriptor : PermissionDescriptor {
  boolean userVisibleOnly = false;
};

4.4. Midi

The "midi"#dom-permissionname-midiReferenced in:4. Permission Registry permission is the permission associated with the usage of [webmidi].

permission descriptor type
dictionary MidiPermissionDescriptor : PermissionDescriptor {
  boolean sysex = false;
};

4.5. Media Devices

The "camera"#dom-permissionname-cameraReferenced in:4. Permission Registry 4.5. Media Devices 10. Examples , "microphone"#dom-permissionname-microphoneReferenced in:4. Permission Registry , and "speaker"#dom-permissionname-speakerReferenced in:4. Permission Registry permissions are associated with permission to use media devices as specified in [GETUSERMEDIA] and [audio-output].

permission descriptor type
dictionary DevicePermissionDescriptor : PermissionDescriptor {
  DOMString deviceId#dom-devicepermissiondescriptor-deviceidReferenced in:4.5. 
      Media Devices
     (2) (3);
};

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".

permission storage type
TODO
permission result type
TODO
permission query algorithm
TODO
permission request algorithm
TODO
permission revocation algorithm
TODO: Stop playing/recording data?

The "device-info"#dom-permissionname-device-infoReferenced in:4. Permission Registry 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.

4.6. Background Sync

The "background-sync"#dom-permissionname-background-syncReferenced in:4. Permission Registry permission is the permission associated with the usage of [web-background-sync].

5. Permission Store #permission-storeReferenced in:4. Permission Registry

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#permission-storage-identifierReferenced in:5. Permission Store (2) (3) (4) (5) (6) which is linked to a PermissionStorage instance or one of its subtypes.

To get a permission storage identifier#get-a-permission-storage-identifierReferenced in:6. Status of a permission 8. Permissions interface (2) for a PermissionName name and an environment settings object settings, the UA MUST return a tuple consisting of:

  1. name
  2. settingsorigin
  3. optional UA-specific data like whether settingsbrowsing context has a parent browsing context, or settingstop-level browsing context’s origin
dictionary PermissionStorage#dictdef-permissionstorageReferenced in:4. 
    Permission Registry
   (2)5. 
    Permission Store
   (2) (3) (4) (5) (6) (7)9. 
    Common permission algorithms
   {
  // PermissionStorage is just an explanatory device.
  // Instances are never received from or passed to Javascript code.

  required PermissionState state;
};

The steps to retrieve a permission storage entry#retrieve-a-permission-storage-entryReferenced in:6. Status of a permission of a permission storage identifier are as follows:

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

The steps to create a permission storage entry#create-a-permission-storage-entryReferenced in:8. Permissions interface for a permission storage identifier are as follows:

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

The steps to delete a permission storage entry#delete-a-permission-storage-entryReferenced in:8. Permissions interface of a permission storage identifier are as follows:

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

6. Status of a permission

enum PermissionState#enumdef-permissionstateReferenced in:5. 
    Permission Store
  6. 
    Status of a permission
   {
  "granted",
  "denied",
  "prompt",
};

The "granted"#dom-permissionstate-grantedReferenced in:4.5. Media Devices 6. Status of a permission 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"#dom-permissionstate-deniedReferenced in:4.5. Media Devices 6. Status of a permission state represents that the caller will not be able to access the feature.

The "prompt"#dom-permissionstate-promptReferenced in:4.5. Media Devices 6. Status of a permission (2) 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 storage#retrieve-the-permission-storageReferenced in:6. Status of a permission 8. Permissions interface for a given PermissionName name are as follows:

  1. Get a permission storage identifier for name and the current environment settings object, and let identifier be the result.
  2. Run the steps to retrieve a permission storage entry of identifier.
  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, {state: "prompt"} can be a default value, but it can also be based on frequency of visits.
[Exposed=(Window,Worker)]
interface PermissionStatus#permissionstatusReferenced in:4. 
    Permission Registry
   (2)6. 
    Status of a permission
   (2) (3)8. 
    Permissions interface
   (2) (3)9. 
    Common permission algorithms
   (2) : EventTarget {
  readonly attribute PermissionState state;
  attribute EventHandler onchange;
};

PermissionStatus instances are created with the following internal slots:

[[permission]]#dom-permissionstatus-permission-slotReferenced in:6. Status of a permission (2) (3)
A permission. [[permission]] is always the permission named [[query]].name.
[[query]]#dom-permissionstatus-query-slotReferenced in:6. Status of a permission (2) (3) (4)
A PermissionDescriptor.

The steps to update the state#update-the-stateReferenced in:6. Status of a permission 8. Permissions interface (2) of a PermissionStatus instance status are as follows:

  1. Run the steps to retrieve the permission storage for status@[[query]].name, and let storage be the result.
  2. Run status@[[permission]]’s permission query algorithm, passing status@[[query]], storage, and status.

The steps to create a PermissionStatus#create-a-permissionstatusReferenced in:8. Permissions interface (2) (3) for a given PermissionDescriptor permissionDesc are as follow:

  1. Let permission be the permission named by permissionDesc.name.
  2. Let status be a new instance of permission’s permission result type, with the internal slots filled as:
    Slot Value
    [[permission]] permission
    [[query]] permissionDesc
  3. Return status.

The state#dom-permissionstatus-stateReferenced in:6. Status of a permission attribute MUST return the latest value that was set on the current instance.

The onchange#dom-permissionstatus-onchangeReferenced in:6. Status of a permission 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 status has changed, it MUST asynchronously run the following steps:

  1. Run the steps to update the state of status.
  2. Queue a task on the permission task source to fire an event named change at status.

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

[Exposed=(Window)]
partial interface Navigator {
  readonly attribute Permissions permissions;
};
[Exposed=(Worker)]
partial interface WorkerNavigator {
  readonly attribute Permissions permissions;
};

8. Permissions interface

[Exposed=(Window,Worker)]
interface Permissions#permissionsReferenced in:4. 
    Permission Registry
   (2) (3)7. 
    Navigator and WorkerNavigator extension
   (2) (3) {
  Promise<PermissionStatus> query(PermissionDescriptor permissionDesc);

  Promise<PermissionStatus> request(PermissionDescriptor permissionDesc);

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

When the query()#dom-permissions-queryReferenced in:4. Permission Registry 8. Permissions interface method is invoked, the user agent MUST run the following query a permission algorithm, passing the parameter permissionDesc:

  1. If permissionDesc.name has a permission descriptor type other than PermissionDescriptor, convert the underlying ECMAScript object to the permission descriptor type dictionary as described in [WEBIDL], then:
    • If that operation failed, return a Promise rejected with a TypeError and abort these steps.
    • Otherwise, set permissionDesc 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 for permissionDesc, and let status be the result.
  5. Run the steps to update the state on status.
  6. Resolve promise with status.
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()#dom-permissions-requestReferenced in:4. Permission Registry 8. Permissions interface method is invoked, the user agent MUST run the following request a permission algorithm, passing the parameter permissionDesc:

  1. If permissionDesc.name has a permission descriptor type other than PermissionDescriptor, convert the underlying ECMAScript object to the permission descriptor type dictionary as described in [WEBIDL], then:
    • If that operation failed, return a Promise rejected with a TypeError and abort these steps.
    • Otherwise, set permissionDesc 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 permission be the permission named by permissionDesc.name.
  5. Run the steps to create a PermissionStatus for permissionDesc, and let status be the result.
  6. Run the steps to retrieve the permission storage for permission, and let storage be the result.
  7. Let result be the result of promise-calling permission’s permission request algorithm with storage, permissionDesc, and status as arguments.
  8. Resolve promise with the result of transforming result with a fulfillment handler that runs the following steps.
  9. Get a permission storage identifier for permissionDesc.name and the current environment settings object, and create a permission storage entry mapping this identifier to storage.
  10. Return status.

When the revoke()#dom-permissions-revokeReferenced in:4. Permission Registry 8. Permissions interface method is invoked, the user agent MUST run the following revoke a permission algorithm, passing the parameter permissionDesc:

  1. If permissionDesc.name has a permission descriptor type other than PermissionDescriptor, convert the underlying ECMAScript object to the permission descriptor type dictionary as described in [WEBIDL], then:
    • If that operation failed, return a Promise rejected with a TypeError and abort these steps.
    • Otherwise, set permissionDesc to the result of the operation.
  2. Let promise be a newly-created Promise.
  3. Return promise and continue the following steps asynchronously.
  4. Get a permission storage identifier for permission.name and the current environment settings object, and let identifier be the result.
  5. Run the steps to delete a permission storage entry using identifier.
  6. Run permissionDesc.name’s permission revocation algorithm.
  7. Run the steps to create a PermissionStatus for permissionDesc, and let status be the result.
  8. Run the steps to update the state on status.
  9. Resolve promise with status.

9. Common permission algorithms

The boolean permission query algorithm#boolean-permission-query-algorithmReferenced in:4. Permission Registry , given a PermissionDescriptor permissionDesc, a PermissionStorage storage, and a PermissionStatus status, runs the following steps:

  1. Set status.state to storage.state

The boolean permission request algorithm#boolean-permission-request-algorithmReferenced in:4. Permission Registry , given a PermissionDescriptor permission and a PermissionStatus status, runs the following steps:

  1. TODO

10. 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.

Conformance

Document conventions

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

Conformant Algorithms

Requirements phrased in the imperative as part of algorithms (such as "strip any leading space characters" or "return false and abort these steps") are to be interpreted with the meaning of the key word ("must", "should", "may", etc) used in introducing the algorithm.

Conformance requirements phrased as algorithms or specific steps can be implemented in any manner, so long as the end result is equivalent. In particular, the algorithms defined in this specification are intended to be easy to understand and are not intended to be performant. Implementers are encouraged to optimize.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[ECMASCRIPT]
ECMAScript Language Specification. URL: https://tc39.github.io/ecma262/
[HTML]
Ian Hickson. HTML Standard. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[PROMISES-GUIDE]
Domenic Denicola. Writing Promise-Using Specifications. 16 February 2016. Finding of the W3C TAG. URL: https://www.w3.org/2001/tag/doc/promises-guide
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://tools.ietf.org/html/rfc2119
[UIEVENTS]
Gary Kacmarcik; Travis Leithead. UI Events Specification. 15 December 2015. WD. URL: https://w3c.github.io/uievents/
[WEBIDL]
Cameron McCormack; Boris Zbarsky. WebIDL Level 1. 8 March 2016. CR. URL: https://heycam.github.io/webidl/
[WHATWG-DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/

Informative References

[AUDIO-OUTPUT]
Justin Uberti; Victoria Kirst. Audio Output Devices API. 10 February 2015. WD. URL: http://www.w3.org/TR/audio-output/
[geolocation-API]
Andrei Popescu. Geolocation API Specification. 28 May 2015. PER. URL: http://www.w3.org/TR/geolocation-API/
[GETUSERMEDIA]
Daniel Burnett; et al. Media Capture and Streams. 14 April 2015. LCWD. URL: http://www.w3.org/TR/mediacapture-streams/
[NOTIFICATIONS]
Anne van Kesteren. Notifications API Standard. Living Standard. URL: https://notifications.spec.whatwg.org/
[PUSH-API]
Michael van Ouwerkerk; et al. Push API. 15 December 2015. WD. URL: http://www.w3.org/TR/push-api/
[WEB-BACKGROUND-SYNC]
Web Background Synchronization specification draft. URL: https://wicg.github.io/BackgroundSync/spec/
[WEBMIDI]
Chris Wilson; Jussi Kalliokoski. Web MIDI API. 17 March 2015. WD. URL: http://www.w3.org/TR/webmidi/

IDL Index

dictionary PermissionDescriptor {
  required PermissionName name;
};

enum PermissionName {
  "geolocation",
  "notifications",
  "push",
  "midi",
  "camera",
  "microphone",
  "speaker",
  "device-info",
  "background-sync",
};

dictionary PushPermissionDescriptor : PermissionDescriptor {
  boolean userVisibleOnly = false;
};

dictionary MidiPermissionDescriptor : PermissionDescriptor {
  boolean sysex = false;
};

dictionary DevicePermissionDescriptor : PermissionDescriptor {
  DOMString deviceId;
};

dictionary PermissionStorage {
  // PermissionStorage is just an explanatory device.
  // Instances are never received from or passed to Javascript code.

  required PermissionState state;
};

enum PermissionState {
  "granted",
  "denied",
  "prompt",
};

[Exposed=(Window,Worker)]
interface PermissionStatus : EventTarget {
  readonly attribute PermissionState state;
  attribute EventHandler onchange;
};

[Exposed=(Window)]
partial interface Navigator {
  readonly attribute Permissions permissions;
};

[Exposed=(Worker)]
partial interface WorkerNavigator {
  readonly attribute Permissions permissions;
};

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

  Promise<PermissionStatus> request(PermissionDescriptor permissionDesc);

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