LayerZeroFault
passkey recovery Updated:

WebAuthn PRF hmac-secret Unsupported Error in Passkey Wallets

VV

Written by

Updated:

Technical Guide

The WebAuthn ‘prf / hmac-secret extension unsupported’ error occurs when a passkey smart wallet attempts to derive deterministic cryptographic seeds from a device authenticator that does not support the FIDO2.1 PRF extension. While Apple Keychain (iOS 17.4+) and Google Password Manager support PRF, older operating systems, hardware keys (YubiKey 5 without PRF), or third-party managers reject the request. Implementing fallback asymmetric signature schemes bypasses the failure.

Before executing state changes or signing transactions, verify your cryptographic parameters using our interactive WebAuthn Passkey Debugger utility.


Why PRF Fails: The Triad Compatibility Gap

For WebAuthn PRF to function, three distinct software and hardware layers must all support it concurrently:

flowchart LR
    A["Browser Engine\n(WebAuthn Level 3)"] --> B["Operating System / Platform\n(Passkey Provider)"]
    B --> C["Hardware Security Enclave\n(CTAP 2.1 hmac-secret)"]

If any single link in this chain is missing, the derivation fails silently:

  1. Browser Layer: While Chrome 116+ and Safari 17.4+ support PRF, Mozilla Firefox currently does not support the PRF extension. Users logging into a Web3 dApp via Firefox receive credential.getClientExtensionResults().prf === undefined.
  2. Platform Layer: Windows Hello on Windows 10/11 supports PRF, and macOS Sonoma/Sequoia supports PRF on iCloud Keychain passkeys. However, Chromium on Linux desktop distributions generally lacks platform PRF support unless connected to a modern hardware security key.
  3. Hardware Roaming Authenticators: If the user selects a physical FIDO2 key (e.g. YubiKey 5 Series), it must have firmware version 5.4.3 or higher to support the CTAP 2.1 hmac-secret command. Older keys (such as YubiKey 4 or early YubiKey 5) reject or ignore the extension during registration.

Step-by-Step Resolution Architecture

1. Robust Runtime Feature & Capability Detection

Never assume the client browser or authenticator supports PRF. You must probe for capability before attempting cryptographic derivation:

// Detect browser PRF extension capability
export function isPrfSupportedByBrowser(): boolean {
  if (typeof window === 'undefined' || !window.PublicKeyCredential) {
    return false;
  }

  // Check if getClientCapabilities exists (Chromium 124+)
  if (typeof (PublicKeyCredential as any).getClientCapabilities === 'function') {
    // Note: getClientCapabilities is asynchronous in modern standards
    return true;
  }

  // User-Agent heuristic fallback for non-supporting browsers
  const ua = navigator.userAgent.toLowerCase();
  if (ua.includes('firefox')) {
    return false; // Firefox currently omits WebAuthn PRF
  }

  return true;
}

2. Requesting the PRF Extension with Defensive Options

When creating credentials (navigator.credentials.create) or requesting assertions (navigator.credentials.get), pass the PRF extension structure correctly and expect undefined:

import { bytesToHex, hexToBytes } from 'viem';

// Salt must be exactly 32 bytes (256 bits)
const DAPP_DERIVATION_SALT = new TextEncoder().encode("layerzerofault-auth-v1-salt-32b");

export async function requestPasskeyPrfKey(credentialId: Uint8Array): Promise<Uint8Array | null> {
  if (!isPrfSupportedByBrowser()) {
    console.warn("Browser lacks PRF extension support. Triggering fallback pathway.");
    return null;
  }

  try {
    const assertion = await navigator.credentials.get({
      publicKey: {
        challenge: crypto.getRandomValues(new Uint8Array(32)),
        allowCredentials: [{
          id: credentialId,
          type: 'public-key',
          transports: ['internal', 'hybrid', 'usb'],
        }],
        userVerification: 'required',
        extensions: {
          prf: {
            eval: {
              first: DAPP_DERIVATION_SALT,
            },
          },
        },
      },
    }) as PublicKeyCredential;

    const extensionResults = assertion.getClientExtensionResults();

    // Defensive check: extension results may exist without prf
    if (!extensionResults.prf || !extensionResults.prf.results || !extensionResults.prf.results.first) {
      console.warn("Authenticator omitted PRF output. Authenticator may lack CTAP 2.1 hmac-secret.");
      return null;
    }

    // Successfully extracted 32-byte symmetric seed
    const derivedSeed = new Uint8Array(extensionResults.prf.results.first);
    return derivedSeed;
  } catch (err: any) {
    console.error("WebAuthn assertion failed:", err);
    return null;
  }
}

3. Graceful Fallback: Hybrid MPC / Enclave Key Derivation

When requestPasskeyPrfKey returns null (due to Firefox, Linux, or an older hardware key), your application must not crash. Instead, route the user to a hybrid fallback recovery scheme:

import { decryptWithWebCrypto, requestMpcCloudShare } from './walletFallback';

export async function unlockUserWallet(credentialId: Uint8Array, userAddress: string) {
  // 1. Attempt hardware-enclave PRF derivation
  const prfSeed = await requestPasskeyPrfKey(credentialId);

  if (prfSeed !== null) {
    // Happy path: Fast, non-custodial local decryption
    const privateKey = await deriveAndDecryptLocalShare(prfSeed, userAddress);
    return { privateKey, source: 'hardware_prf' };
  }

  // 2. Fallback Path: 2-of-3 MPC or Passcode Enclave Recovery
  console.info("Engaging multi-party recovery fallback for unsupported PRF environment...");
  
  // Prompt user for their secondary backup factor (e.g. Passcode or Email OTP)
  const userPasscode = await promptUserForRecoveryPasscode();
  const cloudShare = await requestMpcCloudShare(userAddress, userPasscode);
  const localIndexedDbShare = await loadLocalEncryptedShare(userAddress);

  const restoredKey = reconstructMpcPrivateKey(cloudShare, localIndexedDbShare);
  return { privateKey: restoredKey, source: 'mpc_fallback' };
}

User Communication UX Pattern

When PRF fails due to browser limitations, present a clear, actionable banner rather than an opaque technical error:

Browser Compatibility Notice: Your current browser does not support biometric cryptographic derivation (WebAuthn PRF).

  • To enable seamless one-click biometric login, switch to Google Chrome, Microsoft Edge, or Apple Safari 17.4+.
  • Alternatively, continue using our Encrypted Cloud Recovery Backup below.

By implementing runtime capability detection alongside a robust 2-of-3 MPC or Shamir fallback, you eliminate passkey wallet lockouts across all platforms while preserving top-tier biometric security for supported users.

[!TIP] Companion Diagnostic Guide: If experiencing related infrastructure failures, cross-reference symptoms with our technical breakdown on WebCrypto P-256 SPKI DER Header Mismatch in Passkey Smart Accounts.

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 WebCrypto P-256 SPKI DER Header Mismatch in Passkey Smart Accounts →

Resolve WebCrypto DOMException: Data provided does not meet requirements and on-chain P-256 precompile reverts when converting SPKI DER passkey keys to raw coordinates.

Read Guide
Contextual Architecture

Related Diagnostic Guides

View All in Silo →

Related Inquiries

What is the WebAuthn PRF extension used for in Web3 wallets?

The PRF (Pseudo-Random Function) extension enables authenticators to output deterministic symmetric keys from passkey credentials without exporting private keys. Wallets use this to encrypt account recovery shares or derive child private keys directly from biometrics.

Which browsers and operating systems support WebAuthn PRF?

WebAuthn PRF is supported in Safari 17.4+ on macOS Sonoma and iOS 17.4+, Chrome 116+ on Android and Windows 11 with TPM 2.0, and newer FIDO2.1 hardware authenticators. It is not supported on older Windows 10 builds or legacy security keys.

How should smart contract wallets handle authenticators without PRF?

Wallets must implement capability detection using PublicKeyCredential.getClientCapabilities() or inspect getExtensions().prf. If unsupported, the wallet should fall back to standard P-256 secp256r1 signature verification on-chain.

How can I check if my browser supports the PRF extension?

Run a capability audit with our client-side WebAuthn Passkey Debugger tool to inspect extension flags and authenticator features.