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

PROPOSAL: Alternative Secure Payment Confirmation Flow #15

Closed
adrianhopebailie opened this issue Aug 3, 2020 · 3 comments
Closed

PROPOSAL: Alternative Secure Payment Confirmation Flow #15

adrianhopebailie opened this issue Aug 3, 2020 · 3 comments

Comments

@adrianhopebailie
Copy link
Collaborator

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:

  • Each instrument added by an RP payment handler will support multiple payment methods. In the most common case this will include a payment method identifier that uses the origin of the RP and a payment method identifier of the network.
  • The Payment Method Identifier of the RP is used to invoke the RP Payment Handler directly when the user has already provided payment instrument details and the RP is known by the merchant.
  • The Payment Method Identifier of the network is used to invoke any RP Payment Handler that supports that method and has already installed an instrument in the browser (i.e. prompting the user to select the instrument and thereby invoking the Payment Handler)

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:
HEAD /web-payments/visa-platinum HTTP/2
Host: bankofamerica.com

And the server would then respond:

HTTP/2 204
Link: </pay/payment-manifest.json>; rel="payment-method-manifest"
{
  "default_applications": ["https://bankofamerica.com/web-payments/webappmanifest.json"],
  "supported_origins": ["https://bankofamerica.com"]
}
  • The browser will install the Payment Handler that is defined by the app manifest at https://bankofamerica.com/web-payments/webappmanifest.json

  • If the JIT install fails the PR API will return an error and the merchant falls back to legacy 3DS

  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
Copy link
Collaborator

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
Copy link
Collaborator

Goosth commented Oct 29, 2020

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:

  • Can you do a JIT install of a PH in a hidden 3rd party iFrame?
  • Can you enable a Payment Handler (or Service Worker) in a hidden 3rd party iFrame?

@ianbjacobs
Copy link
Collaborator

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

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

3 participants