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

Make it easy to determine whether a user agent supports SPC #81

Closed
ianbjacobs opened this issue Jun 24, 2021 · 12 comments · Fixed by #233
Closed

Make it easy to determine whether a user agent supports SPC #81

ianbjacobs opened this issue Jun 24, 2021 · 12 comments · Fixed by #233
Labels

Comments

@ianbjacobs
Copy link
Collaborator

Based on conversations with developers, there is a clear desire for an easy mechanism to know whether a browser/user agent supports SPC.

(Related: Issue #65.)

@stephenmcgruer
Copy link
Collaborator

To add some context, we've heard from multiple developers experimenting with the API that the 'standard' way of detecting support for a PaymentMethod (as SPC is currently implemented for the Origin Trial) is too clunky:

const canUseSPC = await (new PaymentRequest([
  {
    supportedMethods: 'secure-payment-confirmation',
    data: {
      credentialIds: [Uint8Array.from(atob('Q1J4AwSWD4Dx6q1DTo0MB21XDAV76'), c => c.charCodeAt(0))],
      challenge: ...,
      timeout: ...,
    },
  }], {total: {label: 'total', amount: {currency: 'USD', value: '20.00'}}})).canMakePayment()

The approach above conflates 'supported in this browser', and 'this PaymentRequest is valid'. Developers often want to answer the former question long before the latter (to decide what flow they will take in their code), and at that point are likely to not even have the credentialIds, the challenge, or maybe even the amount being charged.

@ianbjacobs
Copy link
Collaborator Author

@stephenmcgruer, do you have suggestions for how to accomplish this (and outside of Payment Request)?

@stephenmcgruer
Copy link
Collaborator

@stephenmcgruer, do you have suggestions for how to accomplish this (and outside of Payment Request)?

Yeah, unfortunately it's a tricky case. If we keep PaymentCredential around as an IDL type, it's could be as simple as checking for that existence. But if we succeed in getting closer to WebAuthn, that would be replaced with PublicKeyCredential anyway so one wouldn't be able to feature-detect via it.

We could take the same route as CSS.supports or HTMLMediaElement.canPlayType, which take in descriptors of the feature and return a roughly-boolean answer (canPlayType actually returns 'probably', 'maybe', or an empty-string for definitely-not, I think!). These tend to be viewed as last resorts in API space, as they tend to lead to confusing situations (PaymentRequest.isSupportedMethod('secure-payment-confirmation') returned true, but when I created a PaymentRequest it failed, why?! <-- could happen if the PaymentRequest itself is invalid, etc).

@stephenmcgruer
Copy link
Collaborator

stephenmcgruer commented Jul 22, 2021

Coming back to this; in the way the spec is evolving, PaymentCredential will not be a thing. So checking for that is not going to work.

This leaves a few possibilities:

  1. Rely on canMakePayment, but this is awkward as you have to create a full PR to determine whether it works.
  2. Add an API like isSupportedMethod above, which just takes a method name.

For option 1, an example is https://half-even-onyx.glitch.me/, which uses the following (which is slightly out of date for recent API changes):

    const supportedInstruments = [
      {
        supportedMethods: "secure-payment-confirmation",
        data: {
          action: "authenticate",
          credentialIds: [new Uint8Array(1)],
          networkData: new Uint8Array(1),
          fallbackUrl: window.location
        }
      }
    ];

    const details = {
      total: {label: 'Total', amount: {currency: 'USD', value: '0'}},
    };

    const request = new window.PaymentRequest(supportedInstruments, details);
    return request.canMakePayment().catch(() => false);

This is basically 'guessing' at the minimum data required to call canMakePayment, and passing acceptable junk for it. Not great 😂 .

Edit: Up-to-date version of the feature-detection script for Chrome M95+: https://spc-feature-detect.glitch.me/

@ianbjacobs
Copy link
Collaborator Author

....another reason to decouple this from PR API...

@ianbjacobs
Copy link
Collaborator Author

Labeled after-v1 based on 3 March 2022 WG discussion https://www.w3.org/2022/03/03-wpwg-minutes

@stephenmcgruer
Copy link
Collaborator

stephenmcgruer commented Sep 21, 2022

Coming back to this, and doing some musing. WebAuthn found a similar feature-detection need for conditional UI, and they came up with a static method:

static Promise<boolean> isConditionalMediationAvailable();

Their spec text for it is quite vague (deliberately so, I imagine):

Upon invocation, a promise is returned that resolves with a value of true if conditional user mediation is available, or false otherwise.

We could do something similar, i.e. define on PaymentRequest a new static method:

partial interface PaymentRequest {
    static Promise<boolean> isSecurePaymentConfirmationAvailable();
}

This would allow detection via something like:

const spcAvailable = PaymentRequest && PaymentRequest.isSecurePaymentConfirmationAvailable &&
    await PaymentRequest.isSecurePaymentConfirmationAvailable();

A trickier question is when this should actually return true/false. In Chrome, for now, we would probably return true as long as:

  1. SecurePaymentConfirmation is enabled in the browser, AND
  2. A user-verifying platform authenticator is available (aka, isUserVerifyingPlatformAuthenticatorAvailable() from WebAuthn) AND
  3. We believe the payment extension would work at create() time AND
  4. We believe that SPC authentication would work.

Steps 3 and 4 are interesting ones - we can do this today only because SPC only works for platform authenticators currently. But in a longer-term vision, we want SPC to work for remote authenticators, and at that point not only steps 3 and 4 but also 2 would become invalid - and this check would be reduced (I think?) to:

  1. SecurePaymentConfirmation is enabled in the browser

Which doesn't technically need to be a promise<bool> - we can determine that sychronously.

So the long term value of this API isn't clear to me - but maybe its enough of a pain point for developers that even the short/mid-term ergonomic improvement is sufficient?

One could also argue that we should get off of PaymentRequest entirely, etc etc, but I'm worried that's becoming a 'perfect is the enemy of good' argument and blocking us from making useful progress.

@mikewest
Copy link
Member

Reviewing this as part of the I2S you published a few days ago: the spec text you landed on in #233 doesn't say much of anything about the characteristics that would cause the method to return true. The requirements @stephenmcgruer noted above are also somewhat hand-wavey?

Assuming that 1-4 above remain in place, it would a) be helpful to incorporate them into the spec, (at least as a set of considerations for implementers), and b) helpful to understand how the judgement in 3 and 4 could be made (with an eye towards whether those judgement reveal interesting things about the user or their device).

@stephenmcgruer
Copy link
Collaborator

Hey @mikewest , thanks for the feedback. Some of the difficulty is that these considerations might be different between browsers, as well as the fact that they will definitely evolve as SPC evolves to handle more cases (for example, if/when we change the spec to work with non-platform authenticators, as I note above). We could incorporate them into the spec as non-normative requirements (as you note, as a set of considerations), but we followed the lead of WebAuthn who (for their similar isConditionalMediationAvailable and isUserVerifyingPlatformAuthenticatorAvailable APIs, either didn't specify any steps or explicitly said it was 'client platform-specific'.

helpful to understand how the judgement in 3 and 4 could be made (with an eye towards whether those judgement reveal interesting things about the user or their device).

Can you clarify - are you looking here to understand whether this might be a source of fingerprinting? I am happy to try and answer, but will have to dig up my knowledge from 6+ months ago 😂 . (or maybe @nickburris can comment as he has been in the weeds here lately implementing the API).

@mikewest
Copy link
Member

Hey @stephenmcgruer, thanks for the response!

We could incorporate them into the spec as non-normative requirements (as you note, as a set of considerations), but we followed the lead of WebAuthn who (for their similar isConditionalMediationAvailable and isUserVerifyingPlatformAuthenticatorAvailable APIs, either didn't specify any steps or explicitly said it was 'client platform-specific'.

Clearly, I should have reviewed those additions as well. :)

It seems to me that implementers (including Chrome!) and web developers would benefit from some discussion in the spec of the set of considerations that might cause SPC to be available. It doesn't support remote authenticators at the moment, for instance: the spec's discussion of this limitiation is "this is what Chrome's initial implementation supports", which doesn't provide much in the way of helpful explanation. I hope it wouldn't be too much work to provide a bit more explanation about what user agents ought to take into account when deciding whether SPC is available or not.

are you looking here to understand whether this might be a source of fingerprinting?

Yes. I'd like to understand the delta between this and isUserVerifyingPlatformAuthenticatorAvailable, for example. To that end, understanding what would cause 3 and 4 to work or not would be helpful. :)

@nickburris
Copy link
Member

Yes. I'd like to understand the delta between this and isUserVerifyingPlatformAuthenticatorAvailable, for example. To that end, understanding what would cause 3 and 4 to work or not would be helpful. :)

Conditions 3 and 4 mentioned in Stephen's comment didn't turn out to be any more information than isUserVerifyingPlatformAuthenticatorAvailable. See the design doc which goes into the implementation a bit more, this now boils down to:

  1. SPC is enabled in the browser (with some additional complexity checking whether platform-level credential store APIs are available, currently just on Android).
  2. isUserVerifyingPlatformAuthenticatorAvailable is true.
  3. The payments permission policy is enabled.
  4. The page's security satisfies IsSecurityLevelAcceptableForWebAuthn.

@mikewest
Copy link
Member

Thanks @nickburris, that generally answers my questions.

I think it would be ideal to spell these considerations out a bit more in the specification, but I appreciate you walking me through them here.

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

Successfully merging a pull request may close this issue.

4 participants