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]].
The following concepts and interfaces are defined in [[!HTML]]:
Promise objects are defined in [[!ECMASCRIPT]].
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.
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.
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.
The PermissionName
enum defines the list of known
permission names.
The geolocation
permission is the permission associated with the usage of the
[[geolocation-API]].
The notifications
permission is the permission associated with the usage of the
[[notifications]] API.
The push
permission is the permission associated with the usage of the
[[push-api]]. It has an associated PermissionDescriptor,
PushPermissionDescriptor:
The midi
permission is the permission associated with the usage of
[[webmidi]]. It has an associated PermissionDescriptor,
MidiPermissionDescriptor:
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:
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.
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:
undefined
.The steps to create a permission storage entry for a permission storage identifier are as follows:
The steps to delete a permission storage entry of a permission storage identifier are as follows:
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:
permission
.undefined
, return
it and abort these steps.prompt
can be a default value but it can also be based on frequency of visits.
A PermissionStatus
instance has an associated
permission that is a PermissionDescriptor.
The steps to update the state of a
PermissionStatus
instance are as follows:
PermissionStatus
instance
being updated.
state
attribute.
The steps to create a PermissionStatus for a given
permission
are as follow:
PermissionStatus
instance.
permission
parameter as the
status' associated permission.
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:
PermissionStatus
for
which the state
has changed.
change
at
status.
A Permissions instance is exposed on the navigator
object for Window
and Worker
contexts.
[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:
TypeError
and abort these steps.
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:
When the revoke()
method is invoked, the user agent MUST run the following steps:
TypeError
and abort these steps.
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>
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.