LayerZeroFault
passkey recovery

WebAuthn in Cross-Origin Iframes & Permissions-Policy Blocks

VV

Written by

Published:

Technical Guide

To resolve WebAuthn in Cross-Origin Iframes & Permissions-Policy Blocks, resolve WebAuthn API block errors in cross-origin iframes. Learn how to configure publickey-credentials-get and publickey-credentials-create Permissions-Policy headers. Ensure all derivation paths, environment parameters, and cryptographic signatures are verified client-side using deterministic tools before broadcasting live transactions.

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.

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).

  • 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.

[!TIP] Diagnostic Verification & Air-Gapped Testing: Before executing state changes or broad recovery steps, cross-reference failure parameters with our companion guide on WebAuthn clientDataJSON Origin Mismatch & ERC-4337 Smart Account Reverts and verify transaction calldata or cryptographic envelopes using the WebAuthn & Passkey Enclave Diagnostics Debugger.

Interactive Diagnostic Sandbox

Local WebAuthn & Smart Account Sandboxes

Debug WebAuthn credential requests, PRF extensions, and simulate ERC-4337 passkey account validation in client WebCrypto.

Companion Diagnostic Guide Dynamic XYZ WebAuthn Iframe Storage Partitioning →

Resolve Dynamic XYZ WebAuthn iframe crashes caused by Chrome v120+ storage partitioning. Fix cross-origin blocks using the Storage Access API.

Read Guide
Contextual Architecture

Related Diagnostic Guides

View All in Silo →

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.