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 a way to check what protocols are supported #168

Open
marcoscaceres opened this issue Sep 12, 2024 · 15 comments
Open

Add a way to check what protocols are supported #168

marcoscaceres opened this issue Sep 12, 2024 · 15 comments
Assignees

Comments

@marcoscaceres
Copy link
Collaborator

It's currently not possible to check what protocols the browser supports so developers won't know if calling .get() will immediately fail (as it requires user activation).

As such, we should then add a. static setlike.supportedProtocols With the sequence, a developer can easily filter with standard Set methods.

Proposed API addition

interface DigitalCredentialsSupportedProtocols {
  readonly setlike<DOMString>; // user agent pre-populates
};

partial interface DigitalCredential {
   readonly attribute DigitalCredentialsSupportedProtocols supportedProtocols;
}

Usage:

if (DigitalCredential.supportedProtocols.has("openid4vp")) {
  // let's make an openid4vp request
}

// Or you can do...
const supported = Array.from(DigitalCredential.supportedProtocols).filter(typesTheRPSupports);

// Or whatever developers want:
for (const supported in DigitalCredential.supportedProtocols.values()) {
   // do things with supported
}

const requests = [...DigitalCredential.supportedProtocols].map(toRequestWeKnowHowToMake);

// or even... 
switch (true) {
   case DigitalCredential.supportedProtocols.has("openid4vp"):
      makeOpenIDRequest(data);
      break;
   case DigitalCredential.supportedProtocols.has("whatever"):
      makeWhateverRequest(data);
      break;
   default:
     throw TypeError("Oh noes! they don't support our favorite protocol!")
}

That gives a ton of flexibility. You can even use it will all the new fancy JavaScript set comparison operations:

// DigitalCredential.supportedProtocols is a Set
const supportedProtocols = DigitalCredential.supportedProtocols;

// Example Set of protocols to compare
const protocolsToCheck = new Set(['openid4vp', 'someOtherProtocol']);

// Union: Combining both sets
const union = supportedProtocols.union(protocolsToCheck);
console.log(union); // Set { 'openid4vp', 'someOtherProtocol', ... }

// Intersection: Getting common protocols between the sets
const intersection = supportedProtocols.intersection(protocolsToCheck);
console.log(intersection); // Set { 'openid4vp' }

// Difference: Getting protocols supported by DigitalCredential but not in protocolsToCheck
const difference = supportedProtocols.difference(protocolsToCheck);
console.log(difference); // Set { ... } (protocols supported by DigitalCredential but not in protocolsToCheck)

// Symmetric Difference: Getting protocols that are in either set, but not in both
const symmetricDifference = supportedProtocols.symmetricDifference(protocolsToCheck);
console.log(symmetricDifference); // Set { 'someOtherProtocol', ... }
@msporny
Copy link
Contributor

msporny commented Sep 13, 2024

+1, this is a useful feature to have to ensure that user journeys don't end abruptly when the desired protocol isn't supported.

@timcappalli timcappalli added the cgr1-blocker Community Group Report 1 Blocker label Sep 18, 2024
@timcappalli timcappalli added this to the Community Group Report 1 milestone Sep 19, 2024
@leecam
Copy link
Collaborator

leecam commented Oct 3, 2024

Ideally I'd like browsers on Android to pass the request through and let the OS decide what protocols are supported. So its unclear how a browser could return this list on Android.

I think a isProtocolSupported() method might be better. On Android this would likely always return true.

If we keep this I think it will be very hard for the community to develop or improve the protocols. Lets say OpenID wanted to start work on 'openid4vp_v2'. Today with Chrome/Android they can just start developing and testing it. All they need to do it create a wallet and test website that supports and they can get real implementation experience and iterate before committing it to a stable spec.

We're actually doing that this week by implementing the new query language for openid4vp_v1.1. They need multiple implementations to gain some real world feedback before they can commit to a spec, so we are adding support for it to our wallets and test websites to exercise it. This doesn't require any changes to Android or Chrome.

If we add this API, then browsers couldn't list openid4vp_v1.1 or openid4vp_v2.0 etc...does this mean they shouldn't pass them through? In that case we couldn't iterate on spec proposals without browsers and OSs being in the same iteration loop.

So my recommendation would be to not return a list, but change to a isProtocolSupported('opendi4vp') method. Then on android the browser can defer that question to the platform.

@npdoty
Copy link

npdoty commented Oct 7, 2024

Boolean testing is often preferred to providing a set for privacy reasons.

Also a privacy risk though if the test/set reveals any configuration-specific information (whether you have installed a particular wallet that supports a particular feature, etc.), so we should warn against that if we do support this.

@MasterKale
Copy link

MasterKale commented Oct 7, 2024

It's currently not possible to check what protocols the browser supports so developers won't know if calling .get() will immediately fail (as it requires user activation).

What about an API similar to WebAuthn L3's getClientCapabilities()?

https://w3c.github.io/webauthn/#sctn-getClientCapabilities

Spitballing a bit here, it might look something like this:

const { openid4vp, whatever } = await DigitalCredential.getClientCapabilities();

if (openid4vp) {
  const cred = await navigator.credentials.get({ digital: { ... } });
} else if (whatever) {
  const cred = await someCustomPresentationAPI({ ... });
}

Also a privacy risk though if the test/set reveals any configuration-specific information (whether you have installed a particular wallet that supports a particular feature, etc.), so we should warn against that if we do support this.

There's accommodations in PublicKeyCredential.getClientCapabilities() for browsers to omit certain capabilities for sake of user privacy; I don't see why those same accommodations couldn't be carried over into a Digital Credentials-specific version of the method.

And others can foresee paired use of WebAuthn + Digital Credentials for certain use cases in the future, it might be nice for developers to have similar methods to call in either domain 🤔

So my recommendation would be to not return a list, but change to a isProtocolSupported('opendi4vp') method. Then on android the browser can defer that question to the platform.

To @leecam's question, a getClientCapabilities() method would give browsers a chance to query the platform "just in time" to understand available capabilities too.

@samuelgoto
Copy link
Collaborator

@marcoscaceres and I chatted briefly about this today and so far we think this could work between Chrome and Safari if we introduced the notion of a pre-defined value of * or any to mean "the browser accepts any protocol" (such that Chrome can expose * and Safari reserves the right to skip it).

@timcappalli
Copy link
Member

That seems... messy to be honest. What were the concerns with the method taking an input parameter with a protocol name?

@samuelgoto
Copy link
Collaborator

That seems... messy to be honest. What were the concerns with the method taking an input parameter with a protocol name?

Ah, I remembered why @marcoscaceres and I arrived at *: Chrome allows any protocol to be used, whereas Safari allows some, and it seemed important that Verifiers know the difference between the two in an interoperable way.

Specifically, Chrome deliberately wants to allow protocols to be extensible, so that innovation can happen in this space without it being a blocker.

So, if we go with a a static setlike supportedProtocols as @marcoscaceres proposed, there needs to be an entry there that represents this specific difference between the two implementations.

If we go with getters, we wouldn't need to specify *, which would work too, I think.

I don't personally feel strongly which way we go, just wanted to raise that there is an important distinction between Chrome's implementation and Safari's implementation that's important to expose to developers in an interoperable way.

@timcappalli timcappalli removed the cgr1-blocker Community Group Report 1 Blocker label Nov 18, 2024
@timcappalli timcappalli removed this from the Community Group Report 1 milestone Nov 18, 2024
@kdenhartog
Copy link

kdenhartog commented Nov 19, 2024

Wouldn't this just become a fingerprint vector of which wallets the user has enabled since the browser would just be exposing the protocols supported by the wallet?

For example, if protocol X is only supported by Germany and protocol Y is supported by 3 EU nations, and protocol Z is supported by Bhutan then this becomes a location based inference based on the wallet the user in use. Do we have sufficient evidence that values supplied here aren't diverging based on wallets and country specific regulations and instead the divergence only occurs at the credential format layer? If not, I'd argue this is going to turn into a location based fingerprint and we should limit that risk by not exposing this information.

Also, just to confirm the value here won't be like OIDC4VP+mDL or OIDC4VP+W3C+BBS-SD will it?

A strawman alternative I would suggest would be to decouple the request format passed on via the API from the protocol layer such that the browser translates the API request into the protocol specific format. This way we don't need to reveal the protocol supported.

Also, what's the expected result if there's no matching protocol? Will the user be limited from accessing the site because of this and be forced to switch browsers?

@timcappalli
Copy link
Member

2024-11-18 call: this wouldn't actually provide a lot of value if some clients will just always return true.

A separate issue will be opened for client capabilities (such as cross-device availability, "a wallet being available", etc).

@samuelgoto
Copy link
Collaborator

@marcoscaceres I don't quite remember the use case that led us to want to have the ability to check for protocol support, but I think it was along the lines of the following:

How would a website be able to test, in advance, if a certain protocol (emphasis on protocol, not installed wallet) is supported by the platform?

For example, if a website wanted to degrade gracefully (e.g. throw a custom scheme, hide the UX affordance) in the absence of protocol support in the API, I think they'd have three options:

  1. Make the DC API call and degrade gracefully when it gets an error back
  2. Use a Web Platform API that allows the website to test the support for the protocol ahead of time
  3. Test the user-agent string and special case browser implementations

(3) seems the least great option to me. It is unclear to me if (2) is preferred to (1).

@RByers
Copy link
Member

RByers commented Dec 2, 2024

One possibility for (1) is to define an error code that we always use for such cases of the platform rejecting without showing the user any UI. Separating platform non-support from other types of failure sounds like a good thing to be doing regardless. Given that we know most use cases are going to be falling back to custom schemes in practice anyway, perhaps that's sufficient?

I worry that today many of the hypothetical arguments really boil down to the UI concern that stems from not being able to predict in advance whether a request can be satisfied (where no matching credential is going to be the common case for a long time).

Personally I'd prefer we wait to hear from some developer about a real-world scenario they have where an API like this would actually meaningfully help them (even without a solution to the 'has matching credential' UI problem).

@kdenhartog
Copy link

if a certain protocol (emphasis on protocol, not installed wallet) is supported by the platform?

@samuelgoto could you expand on your thinking here? Since the wallet is effectively processing the request, wouldn't the platform just be effectively revealing the results of what the wallet supports?

For example,

wallet A supports well-known-protocol and cool-new-protocol
wallet B supports well-known-protocol
wallet C supports cool-new-protocol
wallet D supports well-known-protocol

Wouldn't querying this produce a fingerprint like this?

[well-known-protocol] = wallet B or D
[cool-new-protocol] = wallet C
[well-known-protocol, cool-new-protocol] = wallet A

I don't think we want a scenario like this where a site can infer about the existence of an app on the device. Are you saying there's a case where the actual browser platform doesn't support cool-new-protocol irregardless if wallet A or wallet C are in use and in the case of wallet C, it just won't work with that browser?

In any case, if we go the path of option 1 I think we head in the direction of avoiding the fingerprinting concern to a reasonable degree.

@samuelgoto
Copy link
Collaborator

One possibility for (1) is to define an error code that we always use for such cases of the platform rejecting without showing the user any UI. Separating platform non-support from other types of failure sounds like a good thing to be doing regardless. Given that we know most use cases are going to be falling back to custom schemes in practice anyway, perhaps that's sufficient?

I think that would a reasonable variation of (1) or (2).

@samuelgoto
Copy link
Collaborator

samuelgoto commented Dec 3, 2024

Since the wallet is effectively processing the request, wouldn't the platform just be effectively revealing the results of what the wallet supports?

Nope. We are discussing here a test that tests whether the browser supports a specific protocol (Chrome supports any, Safari is planning to support some and block others -- ack @marcoscaceres ?), not whether an installed wallet does.

@sunetzacharias
Copy link

Wouldn't it for the European Digital Identity use case (and maybe others) be as important what the wallet supports? Otherwise it will be difficult for developers and websites to predict and inform the user if conditions aren't met, all they can do then is fail gracefully.

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

No branches or pull requests

10 participants