| Component name | permissions |
|---|---|
| In the visitorId | no (reported, kept out of both ids) |
| structuredId group | none |
| Hashing | always a digest (first 128 bits of SHA-256) |
| Excluded under | no browser class |
| Inner timeout | 1000 ms |
| Added in | signalsVersion 2 |
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
- Without
navigator.permissions.querythe component isunavailable/no_api. - All 24 queries run in parallel.
pushis asked withuserVisibleOnly: true, the only form browsers accept. - Each answer is the state
granted,deniedorprompt; any other state isunknown; a query that throws (the browser does not know that permission) isunsupported. - If every query is
unsupported, the component isunavailable/no_value. - Otherwise the value is
name=state;...in the fixed order above, always hashed (SHA-256, first 128 bits). - The notifications state is also kept internally for the
permissions_inconsistentcheck.
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.queryfor fewer names; the rest come backunsupported. - 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.