Fix the iOS 26.2 WKWebView passkey detection regression where isUserVerifyingPlatformAuthenticatorAvailable() returns false on Chrome.
Vincent
Created: February 3, 2026
Updated: February 4, 2026

Last updated: February 4, 2026
| Date | Event | Status |
|---|---|---|
| Dec 4, 2025 | Chromium bug filed (iOS 26.2 RC) | 📝 Tracked |
| Dec 12, 2025 | iOS 26.2 ships with WKWebView isUVPAA() bug | 🐛 Bug |
| Dec 2025 | Apple Forums: Bug acknowledged | ⚠️ Investigating |
| Jan 26, 2026 | iOS 26.3 beta 3 (23D5114d) fixes WKWebView bug | ✅ Fixed in Beta |
| Jan 29, 2026 | Chrome workaround submitted | 🔧 In Review |
| TBD | Chrome workaround shipped | ⏳ Pending |
| TBD | iOS 26.3 stable release | ⏳ Pending |

Looking for a dev-focused passkey reference? Download our Passkeys Cheat Sheet. Trusted by dev teams at Ally, Stanford CS & more.
With iOS 26.2, Apple introduced a breaking change:
isUserVerifyingPlatformAuthenticatorAvailable() now returns false on all
WKWebView-based browsers (Chrome, Edge, Firefox), even when
passkeys work fine. This affects roughly 10-25% of iOS
users.
In this article, we answer:
isUVPAA() suddenly return false on Chrome
iOS?isUVPAA() vs
getClientCapabilities()?Both APIs answer the same question: "does this device have a platform authenticator (Touch ID, Face ID, Windows Hello) for passkeys?" Here's what each looks like:
isUserVerifyingPlatformAuthenticatorAvailable()#// Returns a simple boolean const isAvailable = await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(); console.log(isAvailable); // Output: true or false
getClientCapabilities()#// Returns an object with multiple capabilities const capabilities = await PublicKeyCredential.getClientCapabilities(); console.log(capabilities); // Output: // { // userVerifyingPlatformAuthenticator: true, // conditionalCreate: true, // conditionalGet: true, // hybridTransport: true, // passkeyPlatformAuthenticator: true, // relatedOrigins: false, // signalAllAcceptedCredentials: true, // signalCurrentUserDetails: true, // signalUnknownCredential: true // }
Why the new API? Introduced in WebAuthn Level 3 (late
2024), getClientCapabilities() unifies feature detection into a single call returning a
capabilities dictionary. Instead of calling multiple methods, you get everything at once.
Sites can tailor UX accordingly, showing passkey options only when supported.
Subscribe to our Passkeys Substack for the latest news.
Browser support comparison (source):
| Browser | isUVPAA() | getClientCapabilities() |
|---|---|---|
| Chrome | 67+ | 133+ |
| Chrome Android | 70+ | 133+ |
| Edge | 18+ | 133+ |
| Firefox | 60+ | 135+ |
| Safari | 13+ | 17.4+ |
| Safari iOS | 13+ | 17.4+ |
| WebView Android | ❌ | ❌ |
| WebView iOS | 13+ | 17.4+ |
By early 2025, getClientCapabilities() covers the vast majority of users on up-to-date
browsers.
Chrome 133+ shipped full support,
and other browsers followed. Older browsers won't have this API, so your detection logic
should handle its absence gracefully. That's where the fallback strategy comes in.
With iOS/macOS 26.2, Apple changed how
isUserVerifyingPlatformAuthenticatorAvailable() behaves. Previously, it almost always
returned true on Apple devices with Touch ID/Face ID, essentially nudging users to
create passkeys even if they hadn't set one up
yet
(we covered the different vendor approaches here).
Now it returns true only if the user has a passkey provider configured
(iCloud Keychain or a third-party
password manager like
1Password). Apple's interpretation
shifted: "available" now means "actively usable," not just "technically present."
Regression bug: During iOS 26.2 beta, developers
noticed that isUVPAA() always
returned false, even on fully passkey-capable devices with Face ID and existing
passkey provider. Apple acknowledged this and fixed it in the
final Safari 26.2 release. However, the bug persisted in WKWebView, the rendering
engine used by all third-party iOS browsers. Any
non-Safari context (Chrome, Edge, Firefox, in-app webviews)
kept returning false regardless of actual device capability.
Become part of our Passkeys Community for updates & support.
Impact was significant: Chrome alone has 10-25% iOS market share depending on region. These users were suddenly misdetected as lacking platform authenticators. The same user who worked fine in Safari was broken in Chrome iOS. Apple's forums got bug reports, and Apple staff confirmed they're investigating.
In summary, iOS 26.2 introduced two distinct changes:
isUVPAA() now requires a configured
passkey provider. Fresh devices return false even with
hardware support. This is by design.Here's where things get interesting. When getClientCapabilities() was introduced, most
developers didn't adopt it for passkey detection. They kept using isUVPAA() for "is
platform authenticator available?" checks since it was
well-supported and worked reliably. New API was primarily adopted for other capabilities
like conditionalCreate, conditionalGet, relatedOrigins, and hybridTransport.
This meant that when both APIs started returning different values for
userVerifyingPlatformAuthenticator, almost nobody noticed.
Want to experiment with passkey flows? Try our Passkeys Debugger.
Divergence actually existed before iOS 26.2, but in opposite direction. Google App on iOS uses an embedded WebView that doesn't support passkeys for third-party domains due to relying party (RP) limitations. This is expected behavior for embedded WebViews. In this environment:
| Platform | isUVPAA() | getClientCapabilities() | What it meant |
|---|---|---|---|
| Google App WebView iOS | false ✅ | true ⚠️ | WebView can't use passkeys |
Here, isUVPAA() was actually correct. Google App WebView
genuinely couldn't perform passkey authentication for third-party RPs, so returning
false was accurate. Meanwhile, getClientCapabilities() returned true because it
reports device hardware capability (iPhone has Face ID), not whether current WebView
context can actually use passkeys for a given RP.
This divergence went unnoticed for two reasons:
false was
expected behaviorWith iOS 26.2, the situation flipped. Now isUVPAA() started returning false on
WKWebView-based browsers (Chrome, Edge, Firefox) even though
passkeys do work in these contexts. Meanwhile, getClientCapabilities() continued
returning true.
| Platform | isUVPAA() | getClientCapabilities() | Status |
|---|---|---|---|
| Safari iOS 26.2 | ⚠️ false if no passkey provider | false | Intended change |
| Chrome/Edge/Firefox iOS | ❌ Always false | true | Bug |
| In-App WKWebView | ❌ Always false | true | Bug |
| Chrome/Edge macOS | ✅ Works correctly | ✅ Works correctly | Unaffected |
| Safari macOS 26.2 | ⚠️ false if no passkey provider | false | Intended change |
| Windows/Android | ✅ Works correctly | ✅ Works correctly | Unaffected |
Important nuance: We can't say getClientCapabilities() is "more accurate" or "tells
truth." What we know is that it continued returning true while isUVPAA() started
returning false. Whether that's because the new API is architecturally better or simply
because Apple didn't change its implementation, we don't know.
For developers, the practical reality is: passkeys do work on Chrome/Edge/Firefox iOS,
and getClientCapabilities() returns true on these browsers. Whether that's by design
or coincidence, the outcome is that you can use it to offer passkey flows where they
actually work.
Want to find out how many people use passkeys?
Given the nuances above, we recommend a conservative approach that accounts for both iOS 26.2 bugs and known WebView limitations.
Pseudo-code:
function canUsePasskeys() { if (isGoogleAppWebView() && isIOS()) { return false; // RP limitations } if (isIOS26_2Plus()) { if (isSafari()) { return isUVPAA(); } else { return getClientCapabilities()?.userVerifyingPlatformAuthenticator ?? false; } } return isUVPAA(); }
1. Google App WebView on iOS: Assume false
Google App uses an embedded WebView where passkeys don't work for third-party RPs due to
platform limitations. Even though getClientCapabilities() returns true (reporting
device capability), passkey authentication will fail. Offer fallback options (cross-device
passkey, password).
2. Safari iOS 26.2+: Use isUVPAA()
On Safari, isUVPAA() works correctly and is now more granular. It returns true only if
a passkey provider (iCloud Keychain,
1Password, etc.) is configured. This
stricter behavior is intentional and gives you a better signal than the capabilities API.
3. Other iOS 26.2+ browsers (Chrome, Edge, Firefox, WKWebView): Use
getClientCapabilities()
On non-Safari iOS 26.2+ browsers, isUVPAA() is broken due to WebKit bug - it always
returns false. Use getClientCapabilities().userVerifyingPlatformAuthenticator instead.
Passkeys do work on these browsers, and the new API happens to return true.
4. Everything else: Use isUVPAA()
For older iOS versions, macOS, Windows, Android, use the legacy API as before.
isUVPAA() (works correctly, more granular)getClientCapabilities() (workaround for bug)isUVPAA()Looking ahead: Once Apple ships a WebKit fix or Chrome's workaround lands in stable, consider reverting to simpler logic. The branching above is a temporary workaround - monitor the timeline and simplify when fixes are confirmed.
Are your users passkey-ready?
In this article, we covered:
What changed in iOS 26.2? Apple's WKWebView now
returns false for isUVPAA() on all third-party browsers, even when passkeys work.
This is an acknowledged bug affecting Chrome, Edge, Firefox, and in-app webviews.
How do the two APIs differ? isUVPAA() is the legacy single-boolean check, while
getClientCapabilities() returns a full capabilities dictionary. On iOS 26.2+, they
diverge: the new API happens to return correct values where the legacy one is broken.
What's the recommended fix? Use platform-aware detection: isUVPAA() for Safari
and older browsers, getClientCapabilities() for non-Safari iOS 26.2+, and assume
false for Google App WebView.
Passkey APIs are evolving rapidly. Platform vendors ship changes, sometimes unintentionally breaking existing behavior. Close monitoring and dynamic handling are essential. When Apple fixes WebKit or Google adds third-party RP support, re-evaluate and simplify your detection logic. The passkeys space moves fast; your implementation should too.
Related Articles
Table of Contents