LayerZeroFault
passkey recovery

Fix: WebAuthn in Cross-Origin Iframes & Permissions-Policy Blocks

VV

Written by

Fact-Checked on July 21, 2026

Verified Expert

Fix: WebAuthn in Cross-Origin Iframes & Permissions-Policy Blocks

If you are building an embedded Web3 application using solutions like Privy, Dynamic, or Web3Auth, you likely rely on an embedded iframe to manage user keys. When implementing passkeys (WebAuthn) inside these cross-origin iframes, you might encounter a critical roadblock: the browser throws a NotAllowedError or a console message declaring: DOMException: The document is not allowed to use this feature.

This happens because modern browsers lock down security access to physical hardware enclaves (like TPMs, TouchID, and FaceID) inside nested contexts. To make WebAuthn operations function properly inside an iframe, you must explicitly declare a Permissions Policy.

If you are also dealing with Chrome 120+ storage partitioning errors in your embedded flows, read our guide on Fixing Dynamic XYZ WebAuthn Iframe Storage Partitioning to resolve cookie and local storage isolation blocks.


Why Browsers Block WebAuthn in Iframes

WebAuthn allows websites to request cryptographic assertions directly from the device’s hardware enclave. Because this action deals with high-stakes user credentials, browsers enforce a strict security perimeter:

  1. Phishing Protection: A malicious site could embed a legitimate Web3 login iframe and capture biometric gestures or spoof the user interface.
  2. Explicit Delegation: The top-level document (your main dApp) must explicitly grant permission to the nested document (the authentication provider’s iframe) to request public key credentials.

If the parent document does not explicitly delegate this capability, any call to navigator.credentials.create() or navigator.credentials.get() within the iframe will immediately throw a security exception.

Placeholder: WebAuthn Iframe Permissions Policy Propagation Diagram


Step-by-Step Fixes to Restore Passkey Functionality

1. Configure the Iframe HTML Attributes

The most direct fix is to update the <iframe> tag inside your HTML markup. You must add the allow attribute and explicitly pass the required WebAuthn tokens:

<!-- Granting WebAuthn permissions to the embedded iframe -->
<iframe 
  src="https://auth.embedded-wallet.xyz" 
  allow="publickey-credentials-get; publickey-credentials-create"
  id="auth-frame"
></iframe>
  • publickey-credentials-get: Required for signing assertions (logging in with an existing passkey).
  • publickey-credentials-create: Required for generating new credentials (registering a new passkey).

If you are using an SDK (such as Privy or Dynamic), ensure you have updated the SDK to its latest version, as older versions might generate iframe elements without these attributes.


2. Set HTTP Permissions-Policy Headers

In addition to the iframe HTML attributes, the parent document’s web server should serve the pages with a matching Permissions-Policy HTTP header. This is a secure fallback that ensures browser extensions or third-party scripts cannot inject unprivileged iframes.

Configure your server (or your Next.js / Astro configuration) to send the following header:

Permissions-Policy: publickey-credentials-get=(self "https://auth.embedded-wallet.xyz"), publickey-credentials-create=(self "https://auth.embedded-wallet.xyz")

This specifies that only the parent domain (self) and the trusted authentication provider (https://auth.embedded-wallet.xyz) are permitted to access the WebAuthn API.


3. Enforce Transient User Activation

Even with correct Permissions-Policy settings, the browser will still block WebAuthn calls inside cross-origin iframes unless they are triggered by Transient User Activation (a direct user gesture).

Placeholder: Browser Console Error Log for Missing WebAuthn Permission

  • Invalid Call Pattern: Calling the WebAuthn API inside a setTimeout, a resolved promise handler, or during an asynchronous fetch callback. The browser will determine that the user’s click event has expired and will block the execution.
  • Valid Call Pattern: The WebAuthn API must be called synchronously inside the click handler:
// Inside the iframe document
loginButton.addEventListener('click', async () => {
  // Sync context within user click event ensures transient activation
  const credential = await navigator.credentials.get({
    publicKey: challengeOptions
  });
  // Handle credential...
});

Troubleshooting Browser Quirks

  • Safari Compatibility: Safari has historically enforced stricter rules than Chrome regarding credential creation inside iframes. If registration fails on iOS/macOS Safari while working on Chrome, ensure that the iframe is completely visible and not hidden using display: none or visibility: hidden at the moment the WebAuthn prompt is triggered.
  • Sandbox Attribute Restrictions: If your <iframe> has a sandbox attribute, you must ensure that it includes both allow-scripts and allow-same-origin. Without allow-same-origin, the iframe is treated as a unique origin and will fail verification checks against the credential server.
Partner Spotlight: Gate.io

Trade Securely on Gate.io

Don't risk your assets on centralized silos or unverified endpoints. Trade securely on Gate.io with deep liquidity and institutional-grade security protocols.

Claim $100 Sign-up Bonus

Official Partner Referral Link

Related Inquiries

Why does the browser throw a NotAllowedError when calling WebAuthn APIs in an iframe?

By default, browser security policies restrict access to publickey-credentials-get and publickey-credentials-create inside cross-origin iframes to protect users from credential phishing. You must explicitly delegate permissions from the parent frame.

How do I delegate WebAuthn capabilities to an embedded iframe?

You must add the 'allow' attribute to the iframe tag with 'publickey-credentials-get; publickey-credentials-create', and ensure the parent page has a matching HTTP header for its Permissions-Policy.

Are user gestures required to trigger WebAuthn inside an iframe?

Yes, browsers strictly enforce transient user activation (such as a direct click or button press) to invoke the WebAuthn API inside cross-origin iframes, preventing background phishing scripts from executing.