The Permission Model
The browser permission model is asymmetric: getting access is a one-time dialog, losing it is permanent. Here is how grants work, why denial is sticky, and how to handle it gracefully.
The first time I accidentally clicked Deny on a geolocation prompt, I expected some kind of undo button. There wasn't one. I had to walk the user through chrome://settings/content/location to fix it -- the app couldn't do a thing.
That asymmetry is intentional.
The permission model is designed to give users control that cannot be taken away by JavaScript. Once a user says no, the browser enforces it at the platform level. No amount of navigator.geolocation.getCurrentPosition() calls will bring the dialog back.
Why Permissions Exist: The Risk Spectrum
Not all capabilities carry the same weight. The browser roughly sorts them into three categories.
Harmless APIs don't need a prompt. Checking the battery level or reading the device orientation doesn't expose anything sensitive, so the browser grants access automatically.
Privacy-touching APIs need explicit consent. Your camera, microphone, and GPS location reveal things users may not want to share. The browser shows a dialog and waits.
Real-cost APIs go further. Some capabilities -- making a phone call, sending an SMS, accessing Bluetooth devices in range -- can cost money or expose hardware. These sit at the highest risk tier.
The Three Permission Models
Not all sensitive APIs use the same mechanism to request access.
Dialog-Based (most APIs)
Most capabilities you'll encounter show a browser dialog. The user sees a prompt: geolocation, camera, microphone, notifications. If they click Allow, the grant applies to the entire origin -- not just the current page or tab.
Silent Engagement Scoring (Background Sync)
Background Sync doesn't show a dialog. Chrome runs a silent scoring algorithm based on how often the user visits your site, how engaged they are, and how reliably they return. There is no UI, no prompt, and no explicit user action. The browser decides.
You can't observe this process or influence it directly. Build for the case where Background Sync isn't granted and you'll be fine.
Auto-Granted (low-risk APIs)
Some capabilities are granted without any interaction. Checking network type, reading device orientation, querying the gamepads connected -- these have no user-visible dialog. If the API exists and the page is on HTTPS, it works.
Requirements Before the Dialog Appears
Even when an API does use a dialog, the browser has prerequisites.
ExpandThe browser permission lifecycle -- from API call through dialog to grant or denial
HTTPS is required. Almost all Chromium capabilities are gated on a secure context. Safari is more permissive here -- it still allows some APIs on HTTP -- but if you're targeting Chrome, lock to HTTPS.
The request must follow a user interaction. You cannot ask for camera access on page load. The call to getUserMedia() or getCurrentPosition() must trace back to a click or a tap. This prevents dark-pattern sites from ambushing users the moment they arrive.
Grants apply to the origin, not the URL. A user who grants geolocation on https://example.com/shop has granted it for all of https://example.com. The path doesn't matter.
What Happens After
Allow sets the permission state to granted for the origin. The duration varies by API. Camera and microphone grants expire at the end of the session -- close the tab and you'll be asked again. Geolocation is granted per usage in some browsers. Some lower-risk APIs hold the grant indefinitely.
Deny is different. The permission state is set to denied, permanently, until the user manually changes it in browser settings. Your code cannot trigger the prompt again. The best you can do is detect the denial and show instructions for how to fix it.
navigator.geolocation.getCurrentPosition(
position => updateMap(position),
error => {
if (error.code === error.PERMISSION_DENIED) {
showSettingsMessage(
'Location access is blocked. To enable it, open your browser settings and allow location for this site.'
);
}
}
);If your feature depends on a permission, treat denial as a terminal state for that session. Guide the user rather than retrying.
Designing Around the Asymmetry
A few practices follow directly from how this model works.
Only ask when the value is obvious to the user. A prompt that appears before they understand why you need it will get denied -- and that denial is permanent.
Give users a clear path when they're blocked. A message that says "location is required" with no instructions for how to re-enable it leaves them stuck.
Check the feature detection pattern from earlier in this series before requesting access -- if the API doesn't exist, there's nothing to request. The maturity tier guide also covers which APIs require user grants versus which are auto-granted.
The next piece of this is controlling which APIs are available at all -- not just on your main page but in any cross-origin iframes you embed. That's where the Permissions-Policy header comes in.
The Essentials
- Browser permissions are user-controlled at the platform level -- JavaScript cannot override a denial.
- Grants apply to the whole origin, not individual pages; duration varies by API (session, per-usage, or indefinite).
- Denial is permanent. Detect it, show instructions for the browser settings page, and don't retry.
- HTTPS and a prior user interaction are prerequisites for the dialog to appear at all.
- Not all sensitive APIs use a dialog -- Background Sync uses a silent Chrome scoring algorithm with no user prompt.
Further Reading and Watching
- How to Manage Permissions Properly (YouTube) -- practical walkthrough of the browser permission UX and what developers can actually control
- Permissions API -- MDN -- the reference spec for querying permission state programmatically
Keep reading