Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PublicKeyCredential.getClientCapabilities - add #37734

Merged
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,19 @@ None.

### Return value

An {{jsxref("Array")}} of strings representing the different transports supported by the authenticator, in lexicographical order. Values may include:

- `"ble"`: The authenticator may be used over [BLE (Bluetooth Low Energy)](https://en.wikipedia.org/wiki/Bluetooth_Low_Energy).
- `"hybrid"`: The authenticator can be used over a combination of (often separate) data transport and proximity mechanisms. This supports, for example, authentication on a desktop computer using a smartphone.
- `"internal"`: The authenticator is specifically bound to the client device (cannot be removed).
- `"nfc"`: The authenticator may be used over [NFC (Near Field Communication)](https://en.wikipedia.org/wiki/Near-field_communication).
- `"usb"`: The authenticator can be contacted over USB.
An {{jsxref("Array")}} of strings representing the different transports supported by the authenticator, in lexicographical order.
Values may include:

- `ble`
- : The authenticator may be used over [BLE (Bluetooth Low Energy)](https://en.wikipedia.org/wiki/Bluetooth_Low_Energy).
- `hybrid`
- : The authenticator can be used over a combination of (often separate) data transport and proximity mechanisms. This supports, for example, authentication on a desktop computer using a smartphone.
- `internal`
- : The authenticator is specifically bound to the client device (cannot be removed).
- `nfc`
- : The authenticator may be used over [NFC (Near Field Communication)](https://en.wikipedia.org/wiki/Near-field_communication).
- `usb`
- : The authenticator can be contacted over USB.

## Examples

Expand Down
15 changes: 11 additions & 4 deletions files/en-us/web/api/credentialscontainer/get/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,20 @@ get(options)

- : A string indicating whether the user will be required to login for every visit to a client app. The value can be one of the following:

- `"conditional"`: Discovered credentials are presented to the user in a non-modal dialog box along with an indication of the origin requesting credentials. In practice, this means autofilling available credentials; see [Sign in with a passkey through form autofill](https://web.dev/articles/passkey-form-autofill) for more details of how this is used; {{domxref("PublicKeyCredential.isConditionalMediationAvailable_static", "PublicKeyCredential.isConditionalMediationAvailable()")}} also provides some useful information.
- `conditional`

- `"optional"`: If credentials can be handed over for a given operation without user mediation, they will be, enabling automatic reauthentication without user mediation. If user mediation is required, then the user agent will ask the user to authenticate. This value is intended for situations where you have reasonable confidence that a user won't be surprised or confused at seeing a login dialog box — for example on a site that doesn't automatically log users in, when a user has just clicked a "Login/Signup" button.
- : Discovered credentials are presented to the user in a non-modal dialog box along with an indication of the origin requesting credentials. In practice, this means autofilling available credentials; see [Sign in with a passkey through form autofill](https://web.dev/articles/passkey-form-autofill) for more details of how this is used; {{domxref("PublicKeyCredential.isConditionalMediationAvailable_static", "PublicKeyCredential.isConditionalMediationAvailable()")}} also provides some useful information.

- `"required"`: The user will always be asked to authenticate, even if prevent silent access (see {{domxref("CredentialsContainer.preventSilentAccess()")}}) is set to `false`. This value is intended for situations where you want to force user authentication — for example if you want a user to reauthenticate when a sensitive operation is being performed (like confirming a credit card payment), or when switching users.
- `optional`

- `"silent"`: The user will not be asked to authenticate. The user agent will automatically reauthenticate the user and log them in if possible. If consent is required, the promise will fulfill with `null`. This value is intended for situations where you would want to automatically sign a user in upon visiting a web app if possible, but if not, you don't want to present them with a confusing login dialog box. Instead, you'd want to wait for them to explicitly click a "Login/Signup" button.
- : If credentials can be handed over for a given operation without user mediation, they will be, enabling automatic reauthentication without user mediation. If user mediation is required, then the user agent will ask the user to authenticate. This value is intended for situations where you have reasonable confidence that a user won't be surprised or confused at seeing a login dialog box — for example on a site that doesn't automatically log users in, when a user has just clicked a "Login/Signup" button.

- `required`

- : The user will always be asked to authenticate, even if prevent silent access (see {{domxref("CredentialsContainer.preventSilentAccess()")}}) is set to `false`. This value is intended for situations where you want to force user authentication — for example if you want a user to reauthenticate when a sensitive operation is being performed (like confirming a credit card payment), or when switching users.

- `silent`
- : The user will not be asked to authenticate. The user agent will automatically reauthenticate the user and log them in if possible. If consent is required, the promise will fulfill with `null`. This value is intended for situations where you would want to automatically sign a user in upon visiting a web app if possible, but if not, you don't want to present them with a confusing login dialog box. Instead, you'd want to wait for them to explicitly click a "Login/Signup" button.

The default value is `"optional"`.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
---
title: "PublicKeyCredential: getClientCapabilities() static method"
short-title: getClientCapabilities()
slug: Web/API/PublicKeyCredential/getClientCapabilities_static
page-type: web-api-static-method
browser-compat: api.PublicKeyCredential.getClientCapabilities_static
---

{{APIRef("Web Authentication API")}}{{securecontext_header}}

The **`getClientCapabilities()`** static method of the {{domxref("PublicKeyCredential")}} interface returns a {{jsxref("Promise")}} that resolves with an object that can be used to check whether or not particular WebAuthn client capabilities and [extensions](/en-US/docs/Web/API/Web_Authentication_API/WebAuthn_extensions) are supported.

A web application can use the information to appropriately customize the user interface and workflows.

## Syntax

```js-nolint
PublicKeyCredential.getClientCapabilities()
```

### Parameters

None.

### Return value

A {{jsxref("Promise")}} that resolves to an object where the property names are the client capability strings, and the values are boolean values that indicate whether or not the corresponding capability or extension is supported.

The WebAuthn client capability strings are:

- `conditionalCreate`
- : The client is capable of conditional mediation when creating/registering credentials.
- `conditionalGet`
- : The client is capable of conditional mediation when authenticating credentials (using [`mediation=conditional`](/en-US/docs/Web/API/CredentialsContainer/get#conditional) in your [`get()`](/en-US/docs/Web/API/CredentialsContainer/get) call).
This means that the client supports workflows where the credentials can be silently fetched/auto-filled on sign in.
This capability is equivalent to [`isConditionalMediationAvailable()`](/en-US/docs/Web/API/PublicKeyCredential/isConditionalMediationAvailable_static) resolving to `true`.
- `hybridTransport`
- : The client supports usage of the [hybrid](/en-US/docs/Web/API/AuthenticatorAttestationResponse/getTransports#hybrid) transport.
This means the the client can use authenticators that rely on Bluetooth, NFC or USB.
- `passkeyPlatformAuthenticator`
- : The client supports usage of a passkey platform authenticator, locally and/or via hybrid transport.
These authenticators enable passwordless multi-factor authentication, providing proof of possession of a credential private key provide and requiring a second authentication mechanism such as a PIN or biometric check.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"providing proof of possession of a credential private key provide" doesn't make sense; rephrase? Not sure if there is a missing word or something.

Also, the Chrome folks have a lot of detailed info about passkey. Maybe consider linking to something like https://developers.google.com/identity/passkeys/developer-guides?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that sux. What it was supposed to say is that you have to have a physical "thing" that grants access (which holds the credential's private key) and you require a user check to use it.

I've updated in https://github.com/mdn/content/pull/37734/files#r1936651860

It is still a little unclear to me if in reality all discoverable credentials are passkeys, and/or what differentiates a passkey from other discoverable credentials. Do you know?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AIUI, passkeys are basically the main available use case for discoverable credentials right now, implemented by Google. Others may be available in the future, hence me saying "for example passkey" in my PR, to future proof the text.

- `userVerifyingPlatformAuthenticator`
- : The client supports usage of a user-verifying platform authenticator.
These require user input for authorization.
- `relatedOrigins`
- : The client supports [Related Origin Requests](https://web.dev/articles/webauthn-related-origin-requests).
These clients allow a passkey to be used across multiple sites that have the same origin.
- `signalAllAcceptedCredentials`
- : The client supports the [`PublicKeyCredential.signalAllAcceptedCredentials()`](/en-US/docs/Web/API/PublicKeyCredential/signalAllAcceptedCredentials_static) static method.
- `signalCurrentUserDetails`
- : The client supports the [`PublicKeyCredential.signalCurrentUserDetails()`](/en-US/docs/Web/API/PublicKeyCredential/signalCurrentUserDetails_static) static method.
- `signalUnknownCredential`
- : The client supports the [`PublicKeyCredential.signalUnknownCredential()`](/en-US/docs/Web/API/PublicKeyCredential/signalUnknownCredential_static) static method.

The [web extension](/en-US/docs/Web/API/Web_Authentication_API/WebAuthn_extensions) strings are formatted by prefixing the [extension identifier](/en-US/docs/Web/API/Web_Authentication_API/WebAuthn_extensions#available_extensions) with the prefix `extension:`.
For example, the key `extension.appid` can be used to check if the [`appid` extension](/en-US/docs/Web/API/Web_Authentication_API/WebAuthn_extensions#appid) is supported.

### Exceptions

The returned {{jsxref("Promise")}} may be rejected with the following values:

- `NotAllowedError` {{domxref("DOMException")}}
- : The Web Authentication API is not allowed in the current browsing context.
For example, it might be blocked by a permission policy.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps be more specific as to which types of permission policy will block it? Maybe link to https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy/publickey-credentials-create and/or https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy/publickey-credentials-get?

I'm not sure if both of them block usage of getClientCapabilities(), or just one.

Copy link
Collaborator Author

@hamishwillee hamishwillee Jan 31, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to leave for now because it is unspecified. Asking in w3c/webauthn#2251, but I don't think this should block merging.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, that's fine.


## Description

The method allows you to check if a given capability or extension is supported, and use the information offer an appropriate user experience.

For example, support for the `userVerifyingPlatformAuthenticator` capability indicates that biometrics such as a fingerprint sensor are allowed.
A web application could use this to display a fingerprint icon if the capability is supported, or a password input if it is not.
If biometric login is required, then it could instead provide notification that the site cannot authenticate using this browser or device.
Similarly, `conditionalGet` indicates that the client supports conditional mediation when signing in a user, which allows for the browser to silently autofill credentials.
In this case the user might simply be offered a sign-in button in order to log in to the site.

If the value of a given capability is present in the map, then `true` indicates that the capability is currently supported, and `false` indicates that it is not.
However if a key is not present for a particular capability, no assumptions can be made about the availability of the associated feature.

For an extension the assumptions are the same, but note that generally if an extension is implemented the value will be `true`.
Note though, that even if the extension is supported by the client a particular authenticator may not support that extension, so RPs must not assume that this is a guarantee that the authenticator processing steps for that extension will be performed.
If the key is not present for an extension then a Relying Party (RP) can't assume that client processing steps for that extension will be carried out by this client or that the extension will be forwarded to the authenticator.

## Examples

### Check all capabilities

This example shows how to get the capabilities object and iterates its values.

```html hidden
<pre id="log"></pre>
<button id="reset" type="button">Reset</button>
```

```js hidden
const logElement = document.querySelector("#log");
function log(text) {
logElement.innerText = `${logElement.innerText}${text}\n`;
logElement.scrollTop = logElement.scrollHeight;
}

const reload = document.querySelector("#reset");

reload.addEventListener("click", () => {
window.location.reload(true);
});
```

```css hidden
#log {
height: 250px;
overflow: scroll;
padding: 0.5rem;
border: 1px solid black;
}
```

#### JavaScript

First we await `getClientCapabilities()` to get an object containing the capabilities.
We then iterate the object and log the result (logging code not shown):

```js
async function checkClientCapabilities() {
const capabilities = await PublicKeyCredential.getClientCapabilities();

if (capabilities) {
log("Client Capabilities:");

for (const [key, value] of Object.entries(capabilities)) {
log(` ${key}: ${value}`);
}
}
}
```

Before calling the function we check that it is defined, and log the result.

```js
// Call the function to check capabilities.
if (typeof PublicKeyCredential.getClientCapabilities === "function") {
checkClientCapabilities();
} else {
log(
"PublicKeyCredential.getClientCapabilities() is not supported on this browser.",
);
}
```

#### Result

{{EmbedLiveSample("Check all capabilities", "", "370")}}

### Test for user verifying platform authenticator

This example checks a single capability `userVerifyingPlatformAuthenticator`, and might then use the result to configure the user interface.

```html hidden
<pre id="log"></pre>
<button id="reset" type="button">Reset</button>
```

```js hidden
const logElement = document.querySelector("#log");
function log(text) {
logElement.innerText = `${logElement.innerText}${text}\n`;
logElement.scrollTop = logElement.scrollHeight;
}

const reload = document.querySelector("#reset");

reload.addEventListener("click", () => {
window.location.reload(true);
});
```

```css hidden
#log {
height: 130px;
overflow: scroll;
padding: 0.5rem;
border: 1px solid black;
}
```

#### JavaScript

The code is similar to the previous example, except that we check a particular returned capability, and we use `try...catch` to catch the case where `getClientCapabilities()` is not supported.

```js
checkisUserVerifyingPlatformAuthenticatorAvailable();

async function checkisUserVerifyingPlatformAuthenticatorAvailable() {
try {
const capabilities = await PublicKeyCredential.getClientCapabilities();

if (capabilities.userVerifyingPlatformAuthenticator) {
log("Biometric login supported");
} else {
log("Biometric login not supported. Do password.");
}
} catch (error) {
if (error instanceof TypeError) {
log(
"PublicKeyCredential.getClientCapabilities() is not supported on this browser.",
);
} else {
log(`Unexpected error: ${error}`);
}
}
}
```

Note that here we log the result of a check.
In a real application we might update the user interface to show appropriate options for verifying the user.

#### Result

The log below displays either a string indicating the method is not supported, or one that indicates whether biometric or password login is supported.

{{EmbedLiveSample("Test for user verifying platform authenticator", "", "200")}}

## Specifications

{{Specifications}}

## Browser compatibility

{{Compat}}

## See also

- [Web Authentication API](/en-US/docs/Web/API/Web_Authentication_API)
2 changes: 2 additions & 0 deletions files/en-us/web/api/publickeycredential/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ The **`PublicKeyCredential`** interface provides information about a public key

## Static methods

- {{domxref("PublicKeyCredential.getClientCapabilities_static", "PublicKeyCredential.getClientCapabilities()")}}
- : Returns a {{jsxref("Promise")}} that resolves with an object that can be used to check whether or not particular WebAuthn capabilities and [extensions](/en-US/docs/Web/API/Web_Authentication_API/WebAuthn_extensions) are supported.
- {{domxref("PublicKeyCredential.isConditionalMediationAvailable_static", "PublicKeyCredential.isConditionalMediationAvailable()")}}
- : Returns a {{jsxref("Promise")}} which resolves to `true` if conditional mediation is available.
- {{domxref("PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable_static", "PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable()")}}
Expand Down
21 changes: 21 additions & 0 deletions files/en-us/web/api/web_authentication_api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,27 @@ A typical authentication flow is as follows:

5. Once verified by the server, the authentication flow is considered successful.

### Customizing workflows based on client capabilities

The signup and login workflows can be customized based on the capabilities of the WebAuthn client (browser), which can be obtained using the {{domxref("PublicKeyCredential.getClientCapabilities_static", "PublicKeyCredential.getClientCapabilities()")}} static method.

This method returns an object with properties named for capabilities or WebAuthn extensions, where the corresponding value indicates whether the feature is supported.
This can be used, for example, to check various kinds of authenticators that the client supports, such as passkeys or biometric user verification, whether the client supports methods to keep relying party and authenticator credentials in sync, or allow a single passkey to be used on different websites with the same origin.

The code below shows how you might use the method to check if the client supports authenticators that offer biometric user verification.

```js
async function checkisUserVerifyingPlatformAuthenticatorAvailable() {
const capabilities = await PublicKeyCredential.getClientCapabilities();
// Check the capability: userVerifyingPlatformAuthenticator
if (capabilities.userVerifyingPlatformAuthenticator) {
log("Biometric login supported");
} else {
log("Biometric login not supported. Do password.");
}
}
```

## Controlling access to the API

The availability of WebAuthn can be controlled using a [Permissions Policy](/en-US/docs/Web/HTTP/Permissions_Policy), specifying two directives in particular:
Expand Down
Loading