-
Notifications
You must be signed in to change notification settings - Fork 208
Explainer: Get Client Capabilities
Tim Cappalli (Okta) <timcappalli@cloudauth.dev>
(with some help from Gemini)
Last updated: 2025-04-29
Currently, WebAuthn Relying Parties (RPs) have limited ways to determine certain WebAuthn client capabilities before initiating credential creation or authentication operations. This lack of upfront knowledge can lead to suboptimal user experiences, where RPs might offer interaction flows that the client (browser or underlying platform) cannot support, or conversely, fail to offer better experiences that are supported.
The getClientCapabilities()
method provides a way for RPs to query the client about a defined set of capabilities, such as support for specific transport mechanisms (e.g., hybrid for cross-device authentication) or UI experiences (e.g., conditional mediation). It also provides a mechanism for clients to advertise support for specific WebAuthn extensions. This allows RPs to tailor their UI, authentication, and registration flows dynamically based on the client's current features, or gracefully fall back to other authentication methods.
Enable Dynamic User Experiences: Allow RPs to adapt their login/registration UI based on known client capabilities before invoking create() or get(). For example, only showing a "Sign in with your phone" button if hybrid transport is supported and there is no local platform authenticator (think a kiosk or smart TV), or choosing between showing a “Sign in with a passkey” button vs. relying solely on autofill UI (conditional UI).
Provide Standardized Capability Discovery: Offer a reliable, spec-defined mechanism for feature detection, avoiding brittle User-Agent string parsing or speculative feature probing, and without adding individual static methods for each capability
Inform Extension Usage: Allow RPs to know if the client implements the client-side processing for a given WebAuthn extension before including that extension identifier in API calls.
Discover Authenticator Capabilities: This API reports on client (browser/platform) capabilities, not the capabilities of any specific authenticator that might be used during a ceremony. Authenticator capabilities are discovered during the create()/get() operations.
Guarantee Feature Success: Reporting true for a capability means the client supports it, but doesn't guarantee it will be successfully utilized in every specific create()/get() call (e.g., the user might cancel, or a specific authenticator chosen might lack a feature the client otherwise supports).
Create a Broad Fingerprinting Surface: The set of standardized capabilities reported should be limited to those with clear UX benefits, and clients have discretion over which capabilities (especially non-standard ones) they report (see Privacy & Security sections).
General Passkey Support: An RP needs to know whether passkeys are generally supported by the client before offering enrollment or sign in flows. For the most basic check, the RP calls getClientCapabilities()
and looks for passkeyPlatformAuthenticator
in the response. If the key is absent or reported as false, the RP may offer other authentication methods or skip passkey enrollment. This was previously accomplished by calling the static method isUserVerifyingPlatformAuthenticatorAvailable()
.
Cross-Device Flow Initiation: An RP wants to offer a prominent "Sign in with your phone" button that initiates a hybrid (caBLE v2) flow. The RP calls getClientCapabilities()
. If the result indicates support for hybrid transport (e.g., {"hybrid": true}
), the RP displays the button. Otherwise, the RP might hide the button or offer an alternative flow.
Conditional UI Optimization: An RP prefers using Conditional Mediation (aka passkey autofill) for sign-in. The RP calls getClientCapabilities()
. If the result indicates Conditional Mediation is not supported (e.g., {"conditionalMediation": false}
or the key is absent), the RP can immediately display a traditional username input field or a “Sign in with a passkey” button instead of waiting for a potential autofill UI that will never appear.
Extension-Specific UI/Logic: An RP utilizes a specific WebAuthn extension (e.g., prf
). Before initiating a create()
or get()
call that includes this extension, the RP calls getClientCapabilities()
. If the result includes {"extension:prf": true}
, the RP proceeds with including the extension; otherwise, it might fall back to a flow that doesn't require the extension or inform the user.
// In partial interface PublicKeyCredential
[SecureContext]
static Promise<PublicKeyCredentialClientCapabilities> getClientCapabilities();
// New typedef
typedef record<DOMString, boolean> PublicKeyCredentialClientCapabilities;
// Enum
enum ClientCapability {
"conditionalCreate", // passkey upgrades
"conditionalGet", // autofill UI
"hybridTransport", // cross-device authentication
"passkeyPlatformAuthenticator", // combo of isUVPAA() + hybridTransports
"userVerifyingPlatformAuthenticator", // mirrors isUVPAA()
"relatedOrigins", // Related Origin Requests
"signalAllAcceptedCredentials", // Signal API capability
"signalCurrentUserDetails", // Signal API capability
"signalUnknownCredential" // Signal API capability
};
-
Invocation: An RP calls
PublicKeyCredential.getClientCapabilities()
. - Client Check: The client (browser/platform) performs internal checks to determine the availability of a predefined set of WebAuthn capabilities and supported extensions. This process is platform-specific.
-
Return Value: The method returns a
Promise
that resolves with aPublicKeyCredentialClientCapabilities
object. -
Structure:
- The keys of the record are
DOMString
values representing specific capabilities or extensions. - Keys representing standard capabilities (in
ClientCapability
) are simple strings (e.g.,"hybrid"
). - Keys representing client support for WebAuthn Extensions MUST be prefixed with
"extension:"
followed by the extension identifier (e.g.,"extension:credProps"
). - The keys in the returned record MUST be sorted in ascending lexicographical order.
- The values are booleans:
-
true
: The client currently supports this capability or extension's client-side processing. -
false
: The client currently does not support this capability or extension.
-
- The keys of the record are
-
Key Omission: If a capability or extension key is not present in the returned record, its availability is unknown or the client chooses not to disclose it. RPs should generally treat an omitted key as equivalent to
false
for the purpose of enabling UI that depends on it. Clients MAY omit keys for various reasons (e.g., privacy, experimental feature status). -
Permissions Policy: Invocation respects the
publickey-credentials-get
Permissions Policy. If the policy disallows usage in the current context, the promise MUST be rejected with aDOMException
whose name is"NotAllowedError"
.
- Why Static? The query is about the client's general capabilities, not tied to a specific credential instance. A static method fits this model best.
- Why Asynchronous? Determining capabilities might require non-trivial platform-specific checks (e.g., querying OS services, checking hardware presence indirectly). An asynchronous Promise-based API prevents blocking the main thread.
- Why a Record/Dictionary? This provides an extensible mechanism. New standard capabilities and extensions can be added over time without changing the method signature.
-
Key Naming and Sorting: Standard capability keys will be defined in the WebAuthn specification. The
extension:
prefix provides a clear namespace for extensions. Lexicographical sorting ensures consistent serialization and ordering, which can be helpful for testing and potentially for caching mechanisms, though caching is not mandated here. - Client Discretion (Key Omission): Allowing clients to omit keys is crucial for privacy (see below) and allows flexibility. For instance, a browser might choose not to report an experimental capability or one deemed too revealing in certain contexts.
-
Extension Reporting: Reporting
{"extension:foo": true}
only indicates the client implements the necessary processing for the "foo" extension. It does not guarantee that any authenticator used subsequently will also support "foo". RPs must still handle cases where an extension is requested but not acted upon by the authenticator.
-
Fingerprinting Surface: Exposing client capabilities increases the potential fingerprinting surface area of the browser/platform.
- Mitigation 1 (Limited Standard Keys): The WebAuthn WG carefully curated the set of standard capabilities exposed, focusing on those with strong justifications for improving user experience that outweigh the fingerprinting risk.
- Mitigation 2 (Client Discretion): The specification explicitly allows clients to omit keys from the result. Browsers can use this discretion to avoid revealing capabilities deemed too identifying, experimental, or sensitive in certain contexts.
-
Mitigation 3 (Permissions Policy): The feature is gated by the existing
publickey-credentials-get
Permissions Policy, meaning it requires a Secure Context and can be disabled by site owners or potentially via user browser settings, similar to the coreget()
operation.
- Information Disclosure: The revealed capabilities (e.g., hybrid support, conditional UI support) are generally related to browser/platform features rather than sensitive user data or specific authenticator details. The direct security risk from knowing these capabilities is considered very low.
- Fingerprinting: This is the primary privacy concern, overlapping significantly with security considerations. The same mitigations apply: limiting standard keys, allowing client omission, and gating via Permissions Policy.
- Minimization: Clients should aim to report the minimum set of capabilities necessary. The ability to omit keys supports this principle. The standard set should be minimal.
- Transparency: It should be clear to developers which capabilities might be reported. Browser developer tools could potentially surface the results of this call for inspection.
- RP Developers: This capability came from direct developer and UX designer feedback
- Browser Vendors: Two browser engines have implemented the method
- User-Agent String Parsing: Highly discouraged. UA strings are unreliable, change frequently, are being frozen/reduced for privacy reasons, and often don't accurately reflect specific WebAuthn feature availability tied to underlying OS services or runtime flags.
-
Attempt Operation and Handle Failure: RPs can try to initiate a flow (e.g., conditional UI
get()
, orcreate()
with hybrid options) and handle errors if the client doesn't support it. This leads to poor UX (e.g., "flickering" UI, confusing error messages, unnecessary user steps) whichgetClientCapabilities()
aims to prevent. -
Expanding Existing Static Methods: Could potentially add flags or options to
isUVPAA()
orisCMA()
. This was deemed less clean and less extensible than a dedicated method returning a dictionary, especially for reporting extension support and future capabilities. - No Action: Maintain the status quo, forcing RPs to use suboptimal approaches (UA sniffing, degraded UX).
This API has no direct impact on accessibility rendering itself. However, it can indirectly improve accessibility by enabling RPs to:
- Present Clearer Choices: By knowing capabilities upfront, RPs can avoid presenting confusing or non-functional UI elements to users. For example, not showing a greyed-out or error-prone "Sign in with phone" button if hybrid is unavailable improves clarity for all users, including those using assistive technologies.
- Optimize Flow: Avoiding unnecessary steps (like waiting for a conditional UI prompt that never comes) can make the authentication process smoother and less confusing.
The RP remains responsible for ensuring the UI it does render based on these capabilities is accessible.