LayerZeroFault
passkey recovery

Fix: Android Credential Manager WebAuthn 'NotAllowedError' in Web3 Passkeys

VV

Written by

Fact-Checked on September 11, 2026

Verified Expert

Fix: Android Credential Manager WebAuthn “NotAllowedError” in Web3 Passkeys

With the adoption of ERC-4337 Smart Accounts and embedded passkey infrastructure (such as Privy, Turnkey, Coinbase Smart Wallet, or ZeroDev), decentralized applications rely heavily on WebAuthn to turn smartphones into non-custodial hardware signers.

On modern Android devices running Android 14 or Android 15, passkeys are orchestrated through the unified Google Credential Manager API. However, developers and mobile users frequently hit a roadblock during wallet creation or signature authorization:

DOMException: The operation either timed out or was not allowed by the user.
  name: "NotAllowedError"
  code: 0
  at navigator.credentials.get (webauthn-client.ts:142)
  at handleSignUserOperation (SmartAccountSigner.tsx:88)

The issue is especially acute when dApps are accessed via Telegram Mini Apps, in-app WebViews (such as Discord or Twitter), or React Native / Flutter wrappers.

Placeholder: Android Credential Manager Passkey Biometric Dialog Failure


1. Root Cause Architecture: How Credential Manager Validates Requests

Under Android 14+, when a Web3 webpage invokes navigator.credentials.create() or navigator.credentials.get(), Chrome does not talk directly to the Linux kernel or hardware secure element. Instead, it delegates the ceremony to Google Play Services Credential Manager:

$$\text{Web Page} \xrightarrow{\text{WebAuthn}} \text{Blink / Chrome} \xrightarrow{\text{IPC}} \text{Credential Manager} \xrightarrow{\text{Biometrics}} \text{TEE / StrongBox Keymaster}$$

The subsystem strictly enforces three validation gates. If any gate fails, Credential Manager aborts execution and dispatches NotAllowedError:

If the ceremony originates from a native Android app embedding a WebView (e.g., a mobile crypto wallet with a built-in browser), Android checks whether the app has established cryptographic ownership over the web domain (rpId). Without an active assetlinks.json verification, the OS considers the WebView an untrusted phishing vector and terminates the ceremony.

Gate 2: Biometric Enclave Lockout Counter

If a user fails fingerprint or facial recognition 5 consecutive times across any application, Android’s KeyguardManager trips an enclave lockout. Subsequent WebAuthn requests are denied automatically without prompting the user, returning NotAllowedError.

Gate 3: WebAuthn Challenge Expiration & Window Focus

The Android system enforces an internal 60-second challenge timeout. If an app performs heavy asynchronous cryptographic work (such as generating zero-knowledge proofs or fetching bundler pre-verification gas limits) before prompting the user, the server challenge expires, leading to an immediate timeout abort.

(For related browser-level cancellation anomalies, see our companion article on WebAuthn AbortError in Passkey Conditional UI).

Placeholder: Diagram of Credential Manager Architecture and Digital Asset Links Gate


2. Step-by-Step Resolution Protocols

Protocol 1: Fix Android Hybrid & WebView Environments

If you are developing a mobile app using React Native, Flutter, or native Kotlin/Java that displays your Web3 dApp:

  1. Do not use raw android.webkit.WebView for passkey auth. Raw WebViews do not pass origin credentials to Credential Manager.
  2. Implement Chrome Custom Tabs (CCT) or Android Custom Tabs: CCT shares the user’s system Chrome session, Google Password Manager, and Passkey enclave.

In React Native:

import * as WebBrowser from 'expo-web-browser';

// Launch passkey onboarding via Chrome Custom Tab instead of internal WebView
export async function openSecurePasskeyAuth(authUrl: string) {
  const result = await WebBrowser.openAuthSessionAsync(authUrl, 'myapp://callback');
  return result;
}
  1. Deploy Digital Asset Links: If you must use WebAuthn in an app that you own, deploy /.well-known/assetlinks.json on your web server’s root:
[
  {
    "relation": [
      "delegate_permission/common.handle_all_urls",
      "delegate_permission/common.get_login_creds"
    ],
    "target": {
      "namespace": "android_app",
      "package_name": "com.layerzerofault.wallet",
      "sha256_cert_fingerprints": [
        "14:6D:E9:7D:0F:52:CC:77:47:31:6E:86:E5:43:CD:28:B4:97:54:15:3A:42:F3:11:DF:E0:BC:23:45:67:89:AB"
      ]
    }
  }
]

Protocol 2: Web3 Frontend Defensive Retry Pattern

In your web application, intercept NotAllowedError and differentiate between a deliberate user cancellation and an OS-level lockout:

export async function authenticatePasskeyWithFallback(
  options: CredentialRequestOptions
): Promise<Credential | null> {
  const startTime = Date.now();

  try {
    const credential = await navigator.credentials.get(options);
    return credential;
  } catch (err: unknown) {
    if (err instanceof DOMException && err.name === 'NotAllowedError') {
      const elapsed = Date.now() - startTime;

      // If the error returns in less than 250ms, the OS rejected the call before the user saw it!
      if (elapsed < 250) {
        console.warn('Android Credential Manager instant lockout detected.');
        throw new Error(
          'Biometric hardware locked or unauthorized WebView detected. Please unlock your device with your phone PIN or open this page in Chrome directly.'
        );
      } else {
        // User saw the prompt and manually tapped "Cancel"
        throw new Error('Authentication cancelled by user.');
      }
    }
    throw err;
  }
}

Protocol 3: End-User Quick-Fix for Biometric Lockout

If you are an end-user stuck in a NotAllowedError loop on Android:

  1. Force a PIN / Pattern Lock:
    • Turn off your phone screen using the physical power button.
    • Wake the screen and swipe to unlock.
    • Do not use fingerprint or face unlock; enter your device PIN or password directly. This resets Android’s Keyguard failure counter.
  2. Verify Google Play Services Updates:
    • Open Settings > Security & Privacy > System & Updates > Google Play system update.
    • Install pending updates to ensure Credential Manager has the latest FIDO2 patches.
  3. Open in Native Chrome:
    • If attempting to sign inside Telegram or Twitter, tap the three vertical dots in the top-right corner and select Open in Chrome.

Placeholder: Infographic of Android Troubleshooting Steps for Passkey Lockout


3. Summary Diagnostic Checklist

  • Verify that rpId matches the second-level domain (e.g., layerzerofault.site instead of sub-paths).
  • Verify that the server challenge has not timed out before navigator.credentials.get() is invoked.
  • Ensure mobile applications launch Chrome Custom Tabs rather than sandboxed WebViews.
  • Confirm that /.well-known/assetlinks.json returns HTTP 200 with Content-Type application/json.
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 Android Credential Manager throw 'NotAllowedError: The operation either timed out or was not allowed by the user'?

On Android 14 and 15, the Google Credential Manager API handles all FIDO2/WebAuthn ceremonies. A NotAllowedError triggers when: (1) the user cancels the biometric prompt, (2) the device has temporarily locked biometrics following five failed face/fingerprint attempts, (3) the caller is an embedded Android WebView that lacks proper Digital Asset Links (assetlinks.json) verification, or (4) the Relying Party ID (rpId) does not match the verified domain origin.

Why do Web3 embedded wallets like Privy and Turnkey fail inside Android mobile apps?

Standard Android WebViews historically block WebAuthn calls by default or fail to resolve the origin correctly, reporting origin as 'null' or 'android-app://<package_name>'. Unless the mobile wrapper implements Chrome Custom Tabs or declares a two-way assetlinks.json association linking the Android app SHA-256 signing fingerprint to the web domain, Credential Manager rejects the assertion with NotAllowedError.

How do I clear an Android biometric lock without rebooting the phone?

If the secure enclave locked biometrics due to repeated failed checks, lock the screen manually, wake the device, and unlock using your Master PIN, Pattern, or Password. This clears the KeyStore biometric failure counter and re-enables passkey cryptographic ceremonies.