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

Add getClientCapabilities method for feature detection #200

Open
timcappalli opened this issue Jan 16, 2025 · 4 comments
Open

Add getClientCapabilities method for feature detection #200

timcappalli opened this issue Jan 16, 2025 · 4 comments
Labels

Comments

@timcappalli
Copy link
Member

timcappalli commented Jan 16, 2025

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.

@npdoty
Copy link

npdoty commented Jan 22, 2025

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.

@timcappalli
Copy link
Member Author

timcappalli commented Jan 22, 2025

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.

@npdoty
Copy link

npdoty commented Jan 22, 2025

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.

@timcappalli
Copy link
Member Author

2025-01-27 call:

  • Supported protocols should be their own method as to support a getter vs an enum pattern (continue with Add a way to check what protocols are supported #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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants