1. Scope of this document
This section is non-normative.
This document specifies a model for permissions to use powerful features on the Web platform and an API to query the current permission state of those features.
Current Web APIs have different ways to deal with permissions. For example, the [notifications] API allows developers to request a permission and check the permission status explicitly. Others 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.
The query()
function provides a tool for developers to
control when permission prompts are shown.
The solution described in this document is meant to be extensible, but isn’t expected to be applicable to all the current and future permissions available in the web platform. Working Groups that are creating specifications whose permission model doesn’t fit in the model described in this document should contact the editors by filing an issue.
2. Privacy considerations
This section is non-normative.
An adversary could use a permission state as an element in creating a
"fingerprint" corresponding to an end-user. Although an adversary can
already determine the state of a permission by actually using the API, that
often leads to a permission request UI being presented to the end-user (if
the permission was not already "granted"
). Thus, even though this API
doesn’t expose new fingerprinting information to websites, it makes it
easier for an adversary to have discreet access to this information. Thus,
implementations are encouraged to have an option for users to block
(globally or selectively) the querying of permission states.
3. Definitions
- New information about the user’s intent
-
The UA may collect information about a user’s intentions in any way its
authors believe is appropriate. This information can come from explicit
user action, aggregate behavior of both the relevant user and other users,
or implicit signals this specification hasn’t anticipated.
The implicit signals could be, for example, the install status of a web application or frequency and recency of visits. A user that has installed a web application and used it frequently and recently is more likely to trust it. Implementations are advised to exercise caution when relying on implicit signals.
- Powerful feature
- A feature of a UA that some code might not be allowed to access, for example because its environment settings object doesn’t satisfy some criteria, or because the user hasn’t given permission.
4. Descriptions of permission requests
dictionaryPermissionDescriptor
{ required PermissionNamename
; };
Each powerful feature has one or more aspects that
websites can request permission to access. To describe these aspects,
each feature defines a subtype of PermissionDescriptor
to be its permission descriptor type.
The "midi"
feature has two aspects: access to normal messages, and
access to system exclusive messages. Thus, its permission descriptor type is:
dictionary MidiPermissionDescriptor : PermissionDescriptor { boolean sysex = false; };
The "bluetooth"
feature lets sites request to access whatever
Bluetooth devices are close to to the user’s device. Thus, its permission descriptor type is:
dictionary BluetoothPermissionDescriptor : PermissionDescriptor { DOMString deviceId; sequence<BluetoothLEScanFilterInit> filters; sequence<BluetoothServiceUUID> optionalServices = []; boolean acceptAllDevices = false; };
General access to Bluetooth devices is represented by {name: 'bluetooth'}
; access to a particular device is represented by {name: 'bluetooth', deviceId: "id"}
; and access to a device with a particular
service is represented by {name: 'bluetooth', filters: [{services: ['service']}]}
5. Permission states
The user agent is responsible for tracking what powerful features each realm has the user’s permission to use. Other specifications can use the operations defined in this section to retrieve the UA’s notion of what permissions are granted or denied, and to ask the user to grant or deny more permissions.
Other specifications can also add more constraints on the UA’s behavior in these algorithms.
Within this section, descriptor is an instance of the permission
descriptor type of the powerful feature named by descriptor.
. name
5.1. Reading the current permission state
A descriptor’s permission state for
an optional environment settings object settings is
the result of the following algorithm, which returns one of "granted"
, "prompt"
, or "denied"
:
- If settings wasn’t passed, set it to the current settings object.
- If settings is a non-secure context and
descriptor.
isn’t allowed in non-secure contexts, then returnname
"denied"
. - If there was a previous invocation of this algorithm with the same descriptor and settings, returning previousResult, and the UA has not received new information about the user’s intent since that invocation, return previousResult.
-
Return whichever of the following options most accurately reflects the user’s
intent for the calling algorithm, taking into account any permission state constraints for
descriptor.
:name
Safari is the only known UA that returns different results from this algorithm for different settings objects with the same origin. We should test which of the several possible settings objects it uses.
As a shorthand, a PermissionName
name’s permission state is
the permission state of a PermissionDescriptor
with its name
member set to name.
Some powerful features have more information associated with them
than just a PermissionState
. For example, getUserMedia()
needs to determine which cameras
the user has granted the current realm permission to access. Each
of these features defines an extra permission data type. If a PermissionName
name names one of these features, then name’s extra permission data for an optional environment
settings object settings is the result of the following algorithm:
- If settings wasn’t passed, set it to the current settings object.
- If there was a previous invocation of this algorithm with the same name and settings, returning previousResult, and the UA has not received new information about the user’s intent since that invocation, return previousResult.
- Return the instance of name’s extra permission data type that matches the UA’s impression of the user’s intent, taking into account any extra permission data constraints for name.
5.2. Requesting more permission
Spec authors, please note that algorithms in this section can wait for user input; so they shouldn’t be used from other algorithms running on the main thread.
To request permission to use a descriptor, the UA
must perform the following steps. This algorithm returns either "granted"
or "denied"
.
- Let current state be the descriptor’s permission state.
- If current state is not
"prompt"
, return current state and abort these steps. - Ask the user’s permission for the calling algorithm to use the powerful feature described by descriptor.
-
If the user grants permission, return
"granted"
; otherwise return"denied"
. The user’s interaction may provide new information about the user’s intent for this realm and other realms with the same origin.This is intentionally vague about the details of the permission UI and how the UA infers user intent. UAs should be able to explore lots of UI within this framework.
As a shorthand, requesting permission to use a PermissionName
name, is the same as requesting permission to use a PermissionDescriptor
with its name
member
set to name.
To prompt the user to choose one of several options associated with a descriptor, the UA must perform the following steps.
This algorithm returns either "denied"
or one of the options.
- If descriptor’s permission state is
"denied"
, return"denied"
and abort these steps. - If descriptor’s permission state is
"granted"
, the UA may return one of options and abort these steps. If the UA returns without prompting, then subsequent prompts for the user to choose from the same set of options with the same descriptor must return the same option, unless the UA receives new information about the user’s intent. - Ask the user to choose one of the options or deny permission, and wait for them to choose. If the calling algorithm specified extra information to include in the prompt, include it.
-
If the user chose an option, return it; otherwise return
"denied"
. If the user’s interaction indicates they intend this choice to apply to other realms, then treat this this as new information about the user’s intent for other realms with the same origin.This is intentionally vague about the details of the permission UI and how the UA infers user intent. UAs should be able to explore lots of UI within this framework.
As a shorthand, prompting the user to choose from options
associated with a PermissionName
name, is the same as prompting
the user to choose from those options associated with a PermissionDescriptor
with its name
member
set to name.
5.3. Reacting to users revoking permission
When the UA learns that the user no longer intends to grant permission for a realm to use a feature, it must queue a task on the Realm’s settings object’s responsible event loop to run that feature’s permission revocation algorithm.
6. Status of a permission
enum PermissionState
{
"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.
[Exposed=(Window,Worker)]
interface PermissionStatus
: EventTarget {
readonly attribute PermissionState state;
attribute EventHandler onchange;
};
PermissionStatus
instances are created with a [[query]]
internal slot, which is an
instance of a feature’s permission descriptor type.
To create a PermissionStatus for a given PermissionDescriptor
permissionDesc, return a new instance of the permission result
type for the feature named by permissionDesc.
,
with the name
[[query]]
internal slot initialized to permissionDesc.
The state
attribute MUST return the latest value that was set 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 status has changed,
it MUST asynchronously run the following steps:
- Run
status@
’s permission query algorithm, passing[[query]]
.name
status@
and status.[[query]]
- Queue a task on the permission task source to fire an event named
change
at status.
7. Navigator and WorkerNavigator extension
[Exposed=(Window)]
partial interface Navigator {
readonly attribute Permissions permissions
;
};
[Exposed=(Worker)]
partial interface WorkerNavigator {
readonly attribute Permissions permissions
;
};
8. Permissions interface
[Exposed=(Window,Worker)] interfacePermissions
{ Promise<PermissionStatus> query(objectpermissionDesc
); };
When the query(permissionDesc)
method is
invoked, the user agent MUST run the following query a
permission algorithm, passing the parameter permissionDesc:
- Let rootDesc be the object permissionDesc refers to, converted to
an IDL value of type
PermissionDescriptor
. If this throws an exception, return a promise rejected with that exception and abort these steps. - Let typedDescriptor be the object permissionDesc refers to, converted to an IDL value of
rootDesc.
’s permission descriptor type. If this throws an exception, return a promise rejected with that exception and abort these steps.name
- Let promise be a newly-created
Promise
. - Return promise and continue the following steps asynchronously.
- Run the steps to create a PermissionStatus for typedDescriptor, and let status be the result.
- Run
status@
’s permission query algorithm, passing[[query]]
.name
status@
and status.[[query]]
- Resolve promise with status.
Promise
.all()
. An example can be
found in the Examples section. 9. Common permission algorithms
The boolean permission query algorithm, given a PermissionDescriptor
permissionDesc and a PermissionStatus
status, runs the following steps:
- Set
status.state
to permissionDesc’s permission state.
10. Permission Registry
enum PermissionName
{
"geolocation",
"notifications",
"push",
"midi",
"camera",
"microphone",
"speaker",
"device-info",
"background-sync",
"bluetooth",
"persistent-storage",
"ambient-light-sensor",
"accelerometer",
"gyroscope",
"magnetometer",
"clipboard",
};
The enumeration values "accelerometer"
, "gyroscope"
and "magnetometer"
are considered provisional and are subject to change
based on feedback from early implementations. See w3c/sensors#22 issue
for more information.
Each enumeration value in the PermissionName
enum identifies a powerful feature. Each powerful feature has the following
permission-related flags, algorithms, and types:
- An allowed in non-secure contexts flag
- By default, only secure contexts can use powerful features. If this flag is set for a feature, the UA may grant access to it in non-secure contexts too.
- A permission descriptor type
-
PermissionDescriptor
or one of its subtypes. If unspecified, this defaults toPermissionDescriptor
.The feature can define a partial order on descriptor instances. If descriptorA is stronger than descriptorB, then if descriptorA’s permission state is
"granted"
, descriptorB’s permission state must also be"granted"
, and if descriptorB’s permission state is"denied"
, descriptorA’s permission state must also be"denied"
.{name:
("midi-with-sysex") is stronger than"midi"
, sysex: true}{name:
("midi-without-sysex"), so if the user denies access to midi-without-sysex, the UA must also deny access to midi-with-sysex, and similarly if the user grants access to midi-with-sysex, the UA must also grant access to midi-without-sysex."midi"
, sysex: false} - Optional permission state constraints
- Constraints on the values that the UA can return as a descriptor’s permission state. Defaults to no constraints beyond the user’s intent.
- An optional extra permission data type
- If specified, the extra permission data algorithm is usable for this feature.
- Optional extra permission data constraints
- Constraints on the values that the UA can return as a
PermissionName
's extra permission data. Defaults to no constraints beyond the user’s intent. - A permission result type
-
PermissionStatus
or one of its subtypes. If unspecified, this defaults toPermissionStatus
. - A permission query algorithm
- Takes an instance of the permission descriptor type 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(permissionDesc)
method and the PermissionStatus update steps. If unspecified, this defaults to the boolean permission query algorithm. - A permission revocation algorithm
- Takes no arguments. Updates any other parts of the implementation that need to be kept in sync with changes in the results of permission states or extra permission data. Run by §5.3 Reacting to users revoking permission. If unspecified, this defaults to doing nothing.
A boolean feature is a powerful feature with all of the above types and algorithms defaulted.
10.1. Geolocation
The "geolocation"
permission is the permission associated with the usage of the [geolocation-API]. It is a boolean feature and is allowed in
non-secure contexts.
10.2. Notifications
The "notifications"
permission is the permission associated with the usage of the [notifications] API. It is a boolean feature and is allowed in
non-secure contexts.
10.3. Push
The "push"
permission is the permission associated with the usage of the [push-api].
- permission descriptor type
-
dictionary
PushPermissionDescriptor
: PermissionDescriptor { booleanuserVisibleOnly
= false; };{name: "push", userVisibleOnly: false}
is stronger than{name: "push", userVisibleOnly: true}
.
10.4. Midi
The "midi"
permission is the permission associated with the usage of [webmidi]. "midi"
is allowed in non-secure contexts.
- permission descriptor type
-
dictionary
MidiPermissionDescriptor
: PermissionDescriptor { booleansysex
= false; };{name: "midi", sysex: true}
is stronger than{name: "midi", sysex: false}
.
10.5. Media Devices
The "camera"
, "microphone"
, and "speaker"
permissions are associated with permission to use media devices as
specified in [GETUSERMEDIA] and [audio-output]. "speaker"
is allowed in non-secure contexts. "camera"
and "microphone"
MAY be allowed in non-secure contexts.
If the current global object has an associated Document
,
and that Document
is not allowed to use the feature indicated
by attribute name allowusermedia
, then the permission
state of any descriptor with a name
of "camera"
or "microphone"
must be "denied"
.
- permission descriptor type
-
dictionary
DevicePermissionDescriptor
: PermissionDescriptor { DOMStringdeviceId
; };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 nodeviceId
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"
.Note that a "granted" permission is no guarantee that getUserMedia will succeed. It only guarantees that the user will not be prompted for permission. There are many other things (such as constraints or the camera being in use) that can cause getUserMedia to fail.
- extra permission data type
- A list of
deviceId
values for the devices the user has made a non-default decision on access to. - permission query algorithm
-
The permission query algorithm runs the following steps:
- If permissionDesc.deviceId exists in the extra permission data,
set
status.state
to permissionDesc’s permission state and terminate these steps. - Let global be a copy of permissionDesc with the
deviceId
member removed. - Set
status.state
to global’s permission state.
- If permissionDesc.deviceId exists in the extra permission data,
set
- permission request algorithm
-
The permission request algorithm runs the following steps:
- Let result be the result of running the boolean permission request algorithm.
- If result is
"granted"
, the UA MUST set the"device-info"
permission to"granted"
for this realm. - The UA MAY treat result as new information about the
user’s intent with respect to the
"device-info"
permission for this realm and other realms with the same origin, provided it doesn’t violate the previous step. - Return result.
- permission revocation algorithm
- When permission for a device is revoked, the UA MUST run the algorithm to stop the track for any other reason than the stop() method being invoked for each MediaStreamTrack sourced from that device.
The "device-info"
permission controls access to the name and capabilities associated with a deviceId
, as well as whether devicechange
events fire when the current browsing
context is not in focus. The "device-info"
permission MUST be revoked
when deviceId
s for an origin are reset.
10.6. Background Sync
The "background-sync"
permission is the permission associated with the usage of [web-background-sync].
10.7. Ambient Light Sensor
The "ambient-light-sensor"
permission is the permission associated with the usage of the [ambient-light] API.
Its permission revocation algorithm is the result of calling generic sensor permission revocation algorithm passing it "ambient-light-sensor" as argument.
10.8. Accelerometer
The "accelerometer"
permission is the permission associated with the usage of the [accelerometer] API.
Its permission revocation algorithm is the result of calling generic sensor permission revocation algorithm passing it "accelerometer" as argument.
10.9. Gyroscope
The "gyroscope"
permission is the permission associated with the usage of the [gyroscope] API.
Its permission revocation algorithm is the result of calling generic sensor permission revocation algorithm passing it "gyroscope" as argument.
10.10. Magnetometer
The "magnetometer"
permission is the permission associated with the usage of the [magnetometer] API.
Its permission revocation algorithm is the result of calling generic sensor permission revocation algorithm passing it "magnetometer" as argument.
10.11. Clipboard
The "clipboard"
permission is the permission associated with the usage of the
asynchronous methods in the [clipboard-apis].
11. 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.
navigator.permissions.query({ name: "geolocation" }).then(({ state }) => { switch (state) { case "granted": showLocalNewsWithGeolocation(); break; case "prompt": showButtonToEnableLocalNews(); break; default: // Don’t do anything if the permission was denied. break; } });
This example is using the "notifications"
permission for a
chat application to show a notification button depending on the
permission state.
function updateNotificationButton(state) { document.getElementById('chat-notification-button') .disabled = (state === 'denied'); } navigator.permissions.query({ name: 'notifications' }).then((result) => { updateNotificationButton(result.state); result.addEventListener('change', () => { updateNotificationButton(result.state); }); });
This example is checking whether the page has the "geolocation"
and the "notifications"
permissions
using
. Promise
.all
Promise.all([ navigator.permissions.query({ name: "geolocation" }), navigator.permissions.query({ name: "notifications" }) ]) .then(([{ state: geoState }, { state: notifState }]) => { console.log("Geolocation permission state is:", geoState); console.log("Notifications permission state is:", notifState); });
This example is checking the permission state of the available cameras.
navigator.mediaDevices
.enumerateDevices()
.then(devices => {
return Promise.all(devices
// filter on video inputs
.filter(({ kind }) => kind === "videoinput")
// map to query object
.map(({ deviceId }) => ({ name: "camera", deviceId }))
// map to query promise
.map(queryObj => navigator.permissions.query(queryObj))
);
})
// log the state or each camera
.then(results => results.forEach(
({ state }, i) => console.log(Camera ${i}: "${state}"
)
))
.catch(
err => console.error(err.stack)
);
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.