To resolve Android Credential Manager WebAuthn NotAllowedError & Lock Fix, fix Android Credential Manager NotAllowedError and. Ensure all derivation paths, environment parameters, and cryptographic signatures are verified client-side using deterministic tools before broadcasting live transactions.
The Android Credential Manager DOMException: NotAllowedError and system prompt Authentication needed: Enter device password or unlock occur during WebAuthn passkey ceremonies when: (1) Android KeyStore enters biometric lockout after repeated sensor failures, (2) an embedded WebView lacks two-way assetlinks.json verification, or (3) the Relying Party ID (rpId) mismatches the origin. Unlocking the phone with the device Master PIN resets the biometric failure counter, while launching via Chrome Custom Tabs prevents WebView origin blocks.
To test your WebAuthn client parameters, origins, and challenge encoding offline, utilize our interactive WebAuthn Passkey Debugger Tool. For related mobile and desktop authentication failures, consult our Coinbase Desktop Passkey Bluetooth Fix and WebAuthn AbortError in Conditional UI.
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:
Gate 1: Digital Asset Links (assetlinks.json)
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).
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:
- Do not use raw
android.webkit.WebViewfor passkey auth. Raw WebViews do not pass origin credentials to Credential Manager. - 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;
}
- Deploy Digital Asset Links:
If you must use WebAuthn in an app that you own, deploy
/.well-known/assetlinks.jsonon 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:
- 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
Keyguardfailure counter.
- 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.
- 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.
- If attempting to sign inside Telegram or Twitter, tap the three vertical dots
3. Summary Diagnostic Checklist
- Verify that
rpIdmatches the second-level domain (e.g.,layerzerofault.siteinstead 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.jsonreturns HTTP200with Content-Typeapplication/json.