Autenticazione con conferma di pagamento sicura

Eiji Kitamura
Eiji Kitamura

I commercianti possono utilizzare la Conferma sicura dei pagamenti (SPC) come parte di una procedura di autenticazione del cliente (SCA) avanzata per una determinata carta di credito o conto bancario. WebAuthn esegue l'autenticazione (spesso tramite la biometria). WebAuthn deve essere registrato in anticipo, come spiegato nella sezione Registrare una conferma di pagamento sicuro.

Come funziona un'implementazione tipica

L'utilizzo più comune delle SPC è quando un cliente effettua un acquisto sul sito di un commerciante e l'emittente della carta di credito o la banca richiedono l'autenticazione del pagatore.

Flusso di lavoro dell'autenticazione.

Esaminiamo la procedura di autenticazione:

  1. Un cliente fornisce le proprie credenziali di pagamento (ad esempio i dati della carta di credito) al commerciante.
  2. Il commerciante chiede all'emittente o alla banca (RP) o emittente corrispondente della credenziale di pagamento se il pagatore necessita di un'autenticazione separata. Questo scambio potrebbe avvenire, ad esempio, con EMV® 3-D Secure.
    • Se la parte soggetta a limitazioni desidera che il commerciante utilizzi SPC e l'utente si è già registrato in precedenza, la parte soggetta a limitazioni risponde con un elenco di ID credenziali registrati dal pagatore e una verifica.
    • Se non è necessaria un'autenticazione, il commerciante può continuare a completare la transazione.
  3. Se è necessaria un'autenticazione, il commerciante determina se il browser supporta le SPC.
    • Se il browser non supporta SPC, procedi con il flusso di autenticazione esistente.
  4. Il commerciante richiama le SPC. Il browser visualizza una finestra di dialogo di conferma.
    • Se non ci sono ID credenziali trasmessi dalla parte soggetta a limitazioni, utilizza il flusso di autenticazione esistente. Dopo l'autenticazione, valuta la possibilità di utilizzare la registrazione SPC per semplificare le autenticazioni future.
  5. L'utente conferma e autentica l'importo e la destinazione del pagamento sbloccando il dispositivo.
  6. Il commerciante riceve una credenziale dall'autenticazione.
  7. La parte soggetta a limitazioni riceve la qualifica dal commerciante e ne verifica l'autenticità.
  8. La parte soggetta a limitazioni invia i risultati della verifica al commerciante.
  9. Il commerciante mostra all'utente un messaggio per indicare se il pagamento è andato a buon fine o non è andato a buon fine.

Rilevamento delle funzionalità

Per rilevare se SPC è supportato sul browser, puoi inviare una chiamata falsa a canMakePayment().

Copia e incolla il codice seguente per consentire il rilevamento di SPC sul sito web di un commerciante.

const isSecurePaymentConfirmationSupported = async () => {
  if (!'PaymentRequest' in window) {
    return [false, 'Payment Request API is not supported'];
  }

  try {
    // The data below is the minimum required to create the request and
    // check if a payment can be made.
    const supportedInstruments = [
      {
        supportedMethods: "secure-payment-confirmation",
        data: {
          // RP's hostname as its ID
          rpId: 'rp.example',
          // A dummy credential ID
          credentialIds: [new Uint8Array(1)],
          // A dummy challenge
          challenge: new Uint8Array(1),
          instrument: {
            // Non-empty display name string
            displayName: ' ',
            // Transparent-black pixel.
            icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+P+/HgAFhAJ/wlseKgAAAABJRU5ErkJggg==',
          },
          // A dummy merchant origin
          payeeOrigin: 'https://non-existent.example',
        }
      }
    ];

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

    const request = new PaymentRequest(supportedInstruments, details);
    const canMakePayment = await request.canMakePayment();
    return [canMakePayment, canMakePayment ? '' : 'SPC is not available'];
  } catch (error) {
    console.error(error);
    return [false, error.message];
  }
};

isSecurePaymentConfirmationSupported().then(result => {
  const [isSecurePaymentConfirmationSupported, reason] = result;
  if (isSecurePaymentConfirmationSupported) {
    // Display the payment button that invokes SPC.
  } else {
    // Fallback to the legacy authentication method.
  }
});

Autenticare l'utente

Per autenticare l'utente, richiama il metodo PaymentRequest.show() con i parametri secure-payment-confirmation e WebAuthn:

Di seguito sono riportati i parametri da fornire alla proprietà data del metodo di pagamento, SecurePaymentConfirmationRequest.

Parametro Descrizione
rpId Il nome host dell'origine RP come ID RP.
challenge Una verifica casuale che impedisce gli attacchi di ripetizione.
credentialIds Un array di ID credenziali. Nell'autenticazione di WebAuthn, la proprietà allowCredentials accetta un array di oggetti PublicKeyCredentialDescriptor, ma nelle SPC trasmetti solo un elenco di ID credenziali.
payeeName (facoltativo) Nome del beneficiario.
payeeOrigin L'origine del beneficiario. Nello scenario sopra menzionato, è l'origine del commerciante.
instrument Una stringa per displayName e un URL per icon che rimanda a una risorsa immagine. Un valore booleano facoltativo (il valore predefinito è true) per iconMustBeShown che specifica un'icona deve essere recuperato e mostrato correttamente affinché la richiesta abbia esito positivo.
timeout Timeout per firmare la transazione in millisecondi
extensions Estensioni aggiunte alla chiamata WebAuthn. Non è necessario specificare personalmente l'estensione "pagamento".

Guarda questo codice di esempio:

// After confirming SPC is available on this browser via a feature detection,
// fetch the request options cross-origin from the RP server.
const options = fetchFromServer('https://rp.example/spc-auth-request');
const { credentialIds, challenge } = options;

const request = new PaymentRequest([{
  // Specify `secure-payment-confirmation` as payment method.
  supportedMethods: "secure-payment-confirmation",
  data: {
    // The RP ID
    rpId: 'rp.example',

    // List of credential IDs obtained from the RP server.
    credentialIds,

    // The challenge is also obtained from the RP server.
    challenge,

    // A display name and an icon that represent the payment instrument.
    instrument: {
      displayName: "Fancy Card ****1234",
      icon: "https://rp.example/card-art.png",
      iconMustBeShown: false
    },

    // The origin of the payee (merchant)
    payeeOrigin: "https://merchant.example",

    // The number of milliseconds to timeout.
    timeout: 360000,  // 6 minutes
  }
}], {
  // Payment details.
  total: {
    label: "Total",
    amount: {
      currency: "USD",
      value: "5.00",
    },
  },
});

try {
  const response = await request.show();

  // response.details is a PublicKeyCredential, with a clientDataJSON that
  // contains the transaction data for verification by the issuing bank.
  // Make sure to serialize the binary part of the credential before
  // transferring to the server.
  const result = fetchFromServer('https://rp.example/spc-auth-response', response.details);
  if (result.success) {
    await response.complete('success');
  } else {
    await response.complete('fail');
  }
} catch (err) {
  // SPC cannot be used; merchant should fallback to traditional flows
  console.error(err);
}

La funzione .show() restituisce un oggetto PaymentResponse tranne details che contiene una credenziale di chiave pubblica con clientDataJSON che contiene i dati della transazione (payment) per la verifica della parte soggetta a limitazioni.

La credenziale risultante deve essere trasferita tra origini alla parte soggetta a limitazioni e verificata.

In che modo la parte soggetta a limitazioni verifica la transazione

La verifica dei dati della transazione presso il server RP è il passaggio più importante del processo di pagamento.

Per verificare i dati della transazione, l'RP può seguire il processo di verifica dell'asserzione dell'autenticazione di WebAuthn. Inoltre, deve verificare il payment.

Un payload di esempio di clientDataJSON:

{
  "type":"payment.get",
  "challenge":"SAxYy64IvwWpoqpr8JV1CVLHDNLKXlxbtPv4Xg3cnoc",
  "origin":"https://spc-merchant.glitch.me",
  "crossOrigin":false,
  "payment":{
    "rp":"spc-rp.glitch.me",
    "topOrigin":"https://spc-merchant.glitch.me",
    "payeeOrigin":"https://spc-merchant.glitch.me",
    "total":{
      "value":"15.00",
      "currency":"USD"
    },
    "instrument":{
      "icon":"https://cdn.glitch.me/94838ffe-241b-4a67-a9e0-290bfe34c351%2Fbank.png?v=1639111444422",
      "displayName":"Fancy Card 825809751248"
    }
  }
}
  • rp corrisponde all'origine della parte soggetta a limitazioni.
  • topOrigin corrisponde all'origine di primo livello prevista dalla parte soggetta a limitazioni (l'origine del commerciante nell'esempio precedente).
  • payeeOrigin corrisponde all'origine del beneficiario che avrebbe dovuto essere mostrata all'utente.
  • total corrisponde all'importo della transazione che avrebbe dovuto essere mostrato all'utente.
  • instrument corrisponde ai dettagli dello strumento di pagamento che avrebbero dovuto essere mostrati all'utente.
const clientData = base64url.decode(response.clientDataJSON);
const clientDataJSON = JSON.parse(clientData);

if (!clientDataJSON.payment) {
  throw 'The credential does not contain payment payload.';
}

const payment = clientDataJSON.payment;
if (payment.rp !== expectedRPID ||
    payment.topOrigin !== expectedOrigin ||
    payment.payeeOrigin !== expectedOrigin ||
    payment.total.value !== '15.00' ||
    payment.total.currency !== 'USD') {
  throw 'Malformed payment information.';
}

Una volta superati tutti i criteri di verifica, la parte soggetta a limitazioni può comunicare al commerciante che la transazione è andata a buon fine.

Passaggi successivi