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 · 0 comments
Open

Add getClientCapabilities method for feature detection #200

timcappalli opened this issue Jan 16, 2025 · 0 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.

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

1 participant