🛡️ Permissions Policy is active — WebAuthn API capability is removed from this page
← All demos · Passkeys Demo 3 of 4 · Permissions Policy

Capability Lockdown — Rogue Analytics

A page that has no business with WebAuthn — but a compromised analytics tag tries to phish a passkey when you click anything. CSP would not help here (the script is from a CDN you allowlisted). Permissions Policy is the defence.

Protected — even though the analytics tag is loaded, the WebAuthn API rejects every call

Acme Inc. — Latest news

A boring marketing page. No login. No account. No passkey functionality. Nothing here should ever invoke WebAuthn.

But a third-party analytics tag has been compromised and is now attempting to register a passkey on your account whenever you interact with the page. Click any of these innocuous-looking buttons to see what happens:

✅ API capability locked down. navigator.credentials.create() rejects with NotAllowedError before any prompt can be shown — the document is not in the API's allowlist. The analytics tag is still loaded and running; it just cannot reach the WebAuthn API. Each attempt fires a Permissions-Policy violation report to Report URI.
Permissions-Policy header sent with this page
publickey-credentials-create=(), publickey-credentials-get=()
What's happening

The directive publickey-credentials-create=() sets the allowlist to empty — no origin, including the page itself, may use the API. The browser enforces this before any JavaScript runs, so there's no race to win, no prompt that briefly flickers. Calls to the API throw immediately and the violation is reported.

Defence-in-depth pattern: for any page that does not legitimately need WebAuthn (homepage, content pages, admin dashboards that don't enroll passkeys), disable both directives. Your login and account-settings pages keep =(self). A compromised script on the locked-down pages cannot pop a passkey prompt regardless of how it got there.

Activity log

Real-time trace of what the analytics tag tried to do, and how the browser responded.

    ← Demo 2 Demo 4 →
    🛡️ CSP blocked 0 attempt(s)