digital-credentials icon indicating copy to clipboard operation
digital-credentials copied to clipboard

Add getClientCapabilities method for feature detection

Open timcappalli opened this issue 11 months ago • 8 comments

Problem Statement

Verifier and issuer developers need to be able to detect a limited set of capabilities prior to rendering / triggering an experience for the user. For example, if a device does not support FIDO CTAP 2.2 hybrid transports for Digital Credentials, the site may wish to fall back to custom schemes or other methods, as the credential type they are requesting is often in a mobile wallet. A developer also needs to know whether the client understands the presentation or issuance protocol (in order for the client to pass the request to the underlying OS for credential or wallet selection).

Proposed Solution

Learning from the evolution of WebAuthn where static methods were added to address individual capabilities, this proposal is to add a getClientCapabilities() method that is inspired by, and closely mirrors the method of the same name in WebAuthn L3.

When the value for a given capability is true, the feature is known to be currently supported by the client (exact meaning is defined with each capability). When the value for a given capability is false, the feature is known to be not currently supported by the client. When a capability does not exist as a key, the availability of the client feature is not known.

Proposed Method

partial interface DigitalCredential {
    static Promise<DigitalCredentialClientCapabilities> getClientCapabilities();
};

typedef record<DOMString, boolean> DigitalCredentialClientCapabilities;

Proposed Capabilities

enum ClientCapability {
  "crossDevice"
}

crossDevice: the client supports usage of FIDO CTAP 2.2's hybrid transports for cross-device presentation and issuance

To start, the only entry in the enum would be crossDevice as defined above. Clients can include protocol support as capabilities in their response, in the format defined below. This would be instead of a dedicated method proposed in #168.

protocol:<protocol URN>: the client knows about this protocol and can forward the request to the downstream OS component

(ex: protocol:urn:openid:protocol:openid4vp:1.0:signed).

To support this, we would not reference the ClientCapability enum. We can add a statement similar to what we did in WebAuthn:

2.1.1. Enumerations as DOMString types Enumeration types are not referenced by other parts of the Web IDL because that would preclude other values from being used without updating this specification and its implementations. It is important for backwards compatibility that client platforms and Relying Parties handle unknown values. > Enumerations for this specification exist here for documentation and as a registry. Where the enumerations are represented elsewhere, they are typed as DOMStrings, for example in transports.

https://w3c.github.io/webauthn/#sct-domstring-backwards-compatibility

Sample Usage

(rough examples)

Influencing page

async function checkForDcApiSupport () {
  // Check for Digital Credentials API support
  if (typeof window.DigitalCredential !== 'undefined') {

    // Check for protocol and cross device support
    if (typeof window.DigitalCredential.getClientCapabilities === 'function') {
      try {
        const capabilities = await window.DigitalCredential.getClientCapabilities();

        if (
          capabilities['crossDevice'] &&
          capabilities['protocol:urn:openid:protocol:openid4vp:1.0:signed']
        ) {
          showButton();
        }
      } catch (error) {
        showQrBasedFlow();
      }
    } else {
      showQrBasedFlow();
    }
  } else {
    showQrBasedFlow();
  }
};

Inline with a call

async function requestCredential() {

  // Check for Digital Credentials API support
  if (typeof window.DigitalCredential !== 'undefined') {

    // Check for protocol and cross device support
    if (typeof window.DigitalCredential.getClientCapabilities === 'function') {
      try {
        const capabilities = await window.DigitalCredential.getClientCapabilities();

        if (
          capabilities['crossDevice'] &&
          capabilities['protocol:urn:openid:protocol:openid4vp:1.0:signed']
        ) {
          try {

            // These parameters are typically fetched from the backend.
            // Statically defined here for protocol extensibility illustration purposes.
            const oid4vp = {
              protocol: "urn:openid:protocol:openid4vp:1.0:signed", // An example of an OpenID4VP request to wallets. // Based on https://github.com/openid/OpenID4VP/issues/125
              data: {
                nonce: "n-0S6_WzA2Mj",
                presentation_definition: {
                  //Presentation Exchange request, omitted for brevity
                },
              },
            };

            // create an Abort Controller
            const controller = new AbortController();

            // Call the Digital Credentials API using the presentation request from the backend
            let dcResponse = await navigator.credentials.get({
              signal: controller.signal,
              mediation: "required",
              digital: {
                requests: [oid4vp]
              }
            });

            // Send the encrypted response to the backend for decryption and verification
            // Ommitted for brevity

          } catch (error) {
            console.error('Error:', error);
          }
        } else {

          // fallback scenario, illustrative only
          fallbackToCustomSchemes();
        }

      } catch (error) {
        console.error('Error:', error);
      }
    } else {
      // fallback scenario, illustrative only
      fallbackToCustomSchemes();
    }
  } else {
    // fallback scenario, illustrative only
    fallbackToCustomSchemes();
  }
};

Privacy Considerations

The privacy considerations would be the same as in WebAuthn:

https://w3c.github.io/webauthn/#sctn-disclosing-client-capabilities

The getClientCapabilities method assists WebAuthn Relying Parties in crafting registration and authentication experiences which have a high chance of success with the client and/or user.

The client’s support or lack of support of a WebAuthn capability may pose a fingerprinting risk. Client implementations MAY wish to limit capability disclosures based on client policy and/or user consent.

timcappalli avatar Jan 16 '25 23:01 timcappalli

I don't understand why the site needs to know whether the user agent supports cross-device presentation of credentials. Isn't the point to abstract away those details and provide a verifiable presentation to the verifier without the verifier having to understand or use different mechanisms if the credential is stored somewhere else?

I would be concerned about fingerprinting and other privacy risk to the extent that these capabilities reflect user-specific configuration or support, rather than just details of whether the user agent has support for a technology. 'I am using a laptop and I have a mobile phone that has a particular wallet functionality turned on' is quite detailed for a drive-by accessible mechanism.

npdoty avatar Jan 22 '25 14:01 npdoty

I don't understand why the site needs to know whether the user agent supports cross-device presentation of credentials.

It is important for developer to be able to guide a user towards a flow that has a high chance of success. If you know up front that a device is not capable of the cross-device presentation flow, taking them through the DC API route just for them to hit a dead end is not a great experience. We learned this first hand with WebAuthn and passkeys, which is why getClientCapabilities was added there after developer feedback. We're running into the same concerns with the Digital Credentials API as we work through the happy and unhappy paths for a user.

I would be concerned about fingerprinting and other privacy risk to the extent that these capabilities reflect user-specific configuration or support, rather than just details of whether the user agent has support for a technology.

This is only the latter: "details of whether the user agent has support for a technology". It conveys nothing about any other device or party.

timcappalli avatar Jan 22 '25 16:01 timcappalli

I don't understand why the site needs to know whether the user agent supports cross-device presentation of credentials.

It is important for developer to be able to guide a user towards a flow that has a high chance of success. If you know up front that a device is not capable of the cross-device presentation flow, taking them through the DC API route just for them to hit a dead end is not a great experience. We learned this first hand with WebAuthn and passkeys, which is why getClientCapabilities was added there after developer feedback. We're running into the same concerns with the Digital Credentials API as we work through the happy and unhappy paths for a user.

I understand the interest in predicting successful flows. But I'm unclear why cross-device presentation is a characteristic that the site can use to determine whether requesting a user's credential will be successful. Can't the user can store their credential on any of their devices, whether it's the device currently being used to browse the verifier's site or some other device?

Sites checking whether cross-device is available and only requesting credentials from cross-device-supporting browsers would seem to punish developers of wallet software that runs on common devices.

npdoty avatar Jan 22 '25 16:01 npdoty

2025-01-27 call:

  • Supported protocols should be their own method as to support a getter vs an enum pattern (continue with #168)
  • Agreement that capability checks should follow the getClientCapabilities style as to avoid user agent sniffing
  • Disagreement on crossDevice being a capability that is advertised, reasons:
    • A more generic capability should be added that conveys "will anything work" (similar to passkeyPlatformAuthenticator in WebAuthn)
    • Some questions over whether this is needed now or should wait for more ecosystem feedback

timcappalli avatar Jan 27 '25 18:01 timcappalli

Sites checking whether cross-device is available and only requesting credentials from cross-device-supporting browsers would seem to punish developers of wallet software that runs on common devices.

From a verifier perspective, it can be helpful to have signals like "browser can use hybrid for DC presentation" to tailor the UI. If hybrid isn't supported then we'd truncate, "click to present a credential from here or another device" to remove the reference to using another device. It may sound like a small thing, but there's a tremendous burden on verifiers to help users understand these novel new flows we're defining. A method like getClientCapabilities() is a great way to give verifiers signals like this in the spirit of helping grow adoption of DC API.

I'm a fan of getCC() for the utility it added to WebAuthn, and think we'd benefit from it equally here as an enabler of more nuanced adoption flows while the DC API continues to permeate out to more and more users, browsers, and platforms.

MasterKale avatar Apr 09 '25 18:04 MasterKale

In the NCCoE session at IIW, further interest was expressed in providing some feature detection capabilities to help verifiers and issuers craft experiences for users without having to just fall back after a user facing failure.

timcappalli avatar Apr 09 '25 22:04 timcappalli

isProtocolSupportedByPlatform sounds helpful to developers and not harmful to privacy.

I still don't understand why the hybrid-ness is something that we want to reveal to the developer or what they would actually learn from that check.

npdoty avatar Apr 11 '25 22:04 npdoty

I still don't understand why the hybrid-ness is something that we want to reveal to the developer or what they would actually learn from that check.

Putting my earlier statement another way, this can help web app developers communicate how credential presentation works to people who aren't used to working with it - if hybrid isn't supported by the browser then a Verifier's website can omit references to it in the moment, or even forego offering presentation completely if the user would always fail on that combination of OS + browser.

It's a UX streamlining consideration, for sake of the people who will actually be using what we're putting together here.

MasterKale avatar Apr 18 '25 21:04 MasterKale