w3c / secure-payment-confirmation

Secure Payment Confirmation (SPC)
https://w3c.github.io/secure-payment-confirmation/
Other
116 stars 39 forks source link

PROPOSAL: Alternative Secure Payment Confirmation Flow #15

Closed adrianhopebailie closed 3 years ago

adrianhopebailie commented 4 years ago

What follows is an alternative flow and API design for SPC that enables a zero-friction flow where the payment network supports this.

Thanks to Gerhard for the idea of using the PReq in this way.

Advantages:

  1. Support for zero-friction flow if RP decides that full WebAuthn flow is not needed
  2. No fingerprinting by RP without explicit RP UI shown to user
  3. Strong privacy model (No data returned to merchant until payment is completed by user)
  4. Can be used with existing 3DS 2.0 backend messages and flows
  5. Secure Payment Confirmation is not invoked by any origin except the RP
  6. No need for browser to maintain instrument to credential mapping
  7. Simpler DX with clear separation of concerns between payment instruments (used to identify a payment account) and credentials (used to identify a user)

Flow without instrument selection

e.g 3DS 2 with legacy card entry form

  1. User provides details of payment instrument (e.g. card details)
  2. Merchant contacts RP to get a URL for the RP payment method manifest
  3. Merchant calls Payment Request using RP URL as payment method
  4. Browser checks for Payment Handler that supports RP payment method and installs JIT if necessary
  5. Browser invokes Payment Handler and passes it details of payment instrument provided by merchant.
  6. Payment Handler does risk assessment on the transaction and if necessary (i.e. zero-friction not possible) gets a list of credential IDs and a challenge to do SPC. (Note: PH is executed in a 1st party context and has access to cookies and local storage in the RP domain. i.e. Good chance that zero-friction is possible despite no fingerprinting)
  7. If zero-friction is possible return response to merchant and merchant completes the payment via the network.
  8. If SPC is required AND there are credential IDs to use the Payment Handler invokes SPC providing the browser with the list of credential IDs and the challenge.
  9. The resulting assertion is returned to the merchant
  10. If the SPC fails the PH calls e.openWindow to render the fallback step-up authentication UI (e.g. SMS-based OTP or similar)

Flow with instrument selection

e.g. 3DS 2 with browser-rendered instrument selection or Google Pay

  1. User clicks pay button and merchant calls Payment Request using a supported network URL(s) as the payment method(s)
  2. Browser checks for instruments installed via Payment Handlers that support provided network payment method(s) and prompt user to select one.
  3. Browser invokes Payment Handler of chosen instrument and passes it details of payment instrument.
  4. Payment Handler does risk assessment on the transaction and if necessary (i.e. zero-friction not possible) gets a list of credential IDs and a challenge to do SPC. (Note: PH is executed in a 1st party context and has access to cookies and local storage in the RP domain. i.e. Good chance that zero-friction is possible despite no fingerprinting)
  5. If zero-friction is possible return response to merchant and merchant completes the payment via the network.
  6. If SPC is required AND there are credential IDs to use the Payment Handler invokes SPC providing the browser with the list of credential IDs and the challenge.
  7. The resulting assertion is returned to the merchant
  8. If the SPC fails the PH calls e.openWindow to render the fallback step-up authentication UI (e.g. SMS-based OTP or similar)

Assumptions:

Example using existing 3DS 2 back-end flows

  1. Alice enters the PAN of her Bank of America VISA card into a merchant website
  2. Using the BIN of her PAN the merchant performs a PReq and gets back the following 3DS Method URL https://bankofamerica.com/web-payments/visa-platinum

NOTE: In a standard 3DS 2 flow the merchant is expected to render an iframe using this URL and the ACS uses this to fingerprint the user's browser. The data collected by the ACS is associated with a threeDSServerTransID generated by the merchant.

  1. The merchant calls Payment Request API using the 3DS Method URL as the payment method identifier and passing Alice's PAN and the 3DS Server Transaction ID it generated in the request.
const request = new PaymentRequest(
  [{
    supportedMethods: 'https://bankofamerica.com/web-payments/visa-platinum',
    data: { 
      cardholderAccountNumber: '512312346523765', 
      threeDSServerTransID: '12345678-1234-5678-abcd-eFABCDEF0123' 
    }
  }],
  {
    total: {
      label: 'total', 
      amount: {currency: 'USD', value: '20.00'}
    }
  });
  1. If the issuers Payment Handler is already installed then it is invoked via the PaymentRequestEvent otherwise the JIT install steps are followed first:

{
  "default_applications": ["https://bankofamerica.com/web-payments/webappmanifest.json"],
  "supported_origins": ["https://bankofamerica.com"]
}
  1. If the payment handler was previously installed or the browser has visited this origin before the PH it will have access to cookies, local storage and WebCrypto APIs to perform some zero-friction checks such as verifying that this is the same device used previously. In Alice's case, she has visited the BofA website in this browser and completed a previous legacy 3DS checkout so she has cookies from bankofamerica.com that are read by the PH. The PH goes online to ACS to get a risk score for the transaction passing the transaction details, PAN and 3DS Server Transaction ID. The request will carry all of the previously installed cookies.

NOTE: The PH is not able to do fingerprinting without calling e.openWindow and showing the user some UI.

NOTE: Other means of doing client-side user identification are possible given the context. e.g. The PH could use local storage or the WebCrypto APIs to securely identify the user without the need for WebAuthn at this stage.

  const response = await fetch("https://bankofamerica.com/3ds/2/risk-check", {
    method: 'POST',
    cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
    credentials: 'same-origin',
    headers: {
      'Content-Type': 'application/json'
    },
    redirect: 'follow',
    body: JSON.stringify({
      threeDSServerTransID: '12345678-1234-5678-abcd-eFABCDEF0123' 
      cardholderAccountNumber: '512312346523765', 
      amount: {currency: 'USD', value: '20.00'}
    })
  })
  const {result, spc} = await response.json()
  1. Alice has no instruments installed in her browser (since the PH was JIT installed) so the PH installs the selected instrument before invoking SPC:
if(result.instrument) {
  await e.instruments.set(
    result.instrument.key, // "98745678-1234-5678-abcd-er76rqwffd86a"
    result.instrument.value);
/* result.instrument.value =
    {
      name: "Visa ****3765",
      icons: [{
        src: "/pay/visa.png",
        sizes: "32x32",
        type: "image/png",
      }],
      methods: [
        "https://visa.com/web-payments/click-to-pay",
        "https://bankofamerica.com/web-payments/visa-platinum"
      ]
    }
  );
*/
}
  1. Since the purchase is of a high-value the ACS requests that SPC be used if available and returns a list of credentials that it knows have been enrolled by Alice on her various devices. The PH invokes SPC.
  if(acsResponse.result === "SKIP-AUTHN"){
    e.respondWith(acsResponse)
    return
  }

  if(acsResponse.result === "DO-SPC"){
    const {challenge, allowCredentials} = spc

    // Internally the browser turns this into a WebAuthn get assertion call
    const assertion = await e.securePaymentConfirmation({
      instrumentId: result.instrument.key,
      challenge,
      allowCredentials,
      60000
    })

    if(assertion){
      e.respondWith(assertion)
      return
    }
  }
  1. If SPC is not possible (e.g. no credentials returned or available) the PH invokes a legacy authN flow
  e.openWindow('https://bankofamerica.com/sms-otp/)
  ....
ianbjacobs commented 4 years ago

Hi @adrianhopebailie,

You wrote above "No fingerprinting by RP without explicit RP UI shown to user." You refer to e.openWindow only in the final step of "Flow without instrument selection." I assume that means that other risk assessment would happen without the user having to see a window. So what is the explicit RP UI shown to the user? Thanks!

Goosth commented 4 years ago

Thanks for the writeup @adrianhopebailie.

The PReq returns a ThreeDSMethodURL. That should however be run as an hidden iFrame and it to a different domain than that of the user:

ianbjacobs commented 3 years ago

I'm going to close this for now since we have reached FPWD.