Signal · signalsVersion 5

permissions

The state of 24 browser permissions (granted, denied, prompt), read with navigator.permissions.query, which never asks the user anything. Hashed, not in either id.

permissions at a glance (from the library code, signalsVersion 5)
Component namepermissions
In the visitorIdno (reported, kept out of both ids)
structuredId groupnone
Hashingalways a digest (first 128 bits of SHA-256)
Excluded underno browser class
Inner timeout1000 ms
Added insignalsVersion 2
In plain words

Browsers remember which sites may use the camera, send notifications and so on. A page can ask the browser for the current state of a permission without asking the user anything. WhiskerJS asks for 24 of them and keeps only a short hash of the answers. It never requests a permission and never shows a prompt.

What it reads

The state of each of these permissions from navigator.permissions.query({ name }): accelerometer, background-fetch, background-sync, camera, clipboard-read, clipboard-write, display-capture, geolocation, gyroscope, idle-detection, keyboard-lock, local-fonts, magnetometer, microphone, midi, nfc, notifications, payment-handler, persistent-storage, pointer-lock, push, screen-wake-lock, storage-access, window-management.

How the technique works

  1. Without navigator.permissions.query the component is unavailable / no_api.
  2. All 24 queries run in parallel. push is asked with userVisibleOnly: true, the only form browsers accept.
  3. Each answer is the state granted, denied or prompt; any other state is unknown; a query that throws (the browser does not know that permission) is unsupported.
  4. If every query is unsupported, the component is unavailable / no_value.
  5. Otherwise the value is name=state;... in the fixed order above, always hashed (SHA-256, first 128 bits).
  6. The notifications state is also kept internally for the permissions_inconsistent check.

Why it helps

Two things vary: which permission names the browser knows (a browser and version fingerprint of its own) and what the user has granted or refused. Both are useful to your server as context. The second is why it stays out of the ids.

Stability

Medium: the set of known names changes with browser updates, and the states change whenever the user grants or revokes something. Neither moves the visitorId or the structuredId.

Browser support and what blocks it

  • Chromium browsers (Chrome, Edge, Brave, Samsung Internet) know most of the names.
  • Firefox and Safari implement navigator.permissions.query for fewer names; the rest come back unsupported.
  • On slow or emulated setups the parallel queries can exceed the 1000 ms inner timeout; then the component is timed_out.

No browser class excludes it.

Privacy

Permission states can say a little about how someone uses a site (for example that they allowed notifications). WhiskerJS reads them only, never asks, and reports a digest rather than the list. If you do not need it, leave it out with collect({ exclude: ['permissions'] }); since it is outside the ids, that does not change any id.

How WhiskerJS handles noise and failures

Status ruleId When
ok digest of the 24 states
unavailable no_api no navigator.permissions.query
unavailable no_value every query was unsupported
unavailable no_digest no crypto.subtle
excluded caller_excluded you excluded it
timed_out timeout, aborted not all queries answered in time, or budget or abort

Example value

In the container test, headless Chromium gave bcecb787af4b911800b26cdac1d7b6ea and Firefox 3fb80d81dd1f71b5a083dc4dd324ada7; in headless WebKit the queries did not finish within 1000 ms and the component was timed_out / timeout. The same Chromium run also set permissions_inconsistent.

Questions and answers

Will my visitors see a permission prompt?

No. navigator.permissions.query only reports the current state. WhiskerJS never calls an API that requests a permission, such as getUserMedia, geolocation or Notification.requestPermission.

Why are permissions not in the id?

The state changes when the user grants or revokes a permission, which is a normal thing to do. In the id, that would change it.

What is permissions_inconsistent?

An integrity flag that fires when Notification.permission is denied while the permissions query for notifications says prompt, a trait known from headless Chrome. Normal browsers keep the two in step.