Untuk mendapatkan kunci keamanan Anda buka g co sc di jendela browser baru

Kemudian, Anda akan menambahkan dukungan untuk autentikasi 2 langkah melalui kunci keamanan, berdasarkan WebAuthn. Untuk melakukannya, Anda harus mengimplementasikan hal berikut:

Show
  • Cara bagi pengguna untuk mendaftarkan kredensial WebAuthn.
  • Alur autentikasi 2 langkah saat pengguna diminta memasukkan faktor kedua—kredensial WebAuthn—jika telah mendaftarkannya.
  • Antarmuka pengelolaan kredensial: daftar kredensial yang memungkinkan pengguna mengganti nama dan menghapus kredensial.

Untuk mendapatkan kunci keamanan Anda buka g co sc di jendela browser baru

Lihat aplikasi web yang telah selesai dan cobalah.

2. Tentang WebAuthn

Dasar-dasar WebAuthn

Mengapa WebAuthn?

Phishing adalah masalah keamanan yang sangat serius di web: sebagian besar pelanggaran akun memanfaatkan sandi yang lemah atau dicuri yang digunakan kembali di berbagai situs. Respons kolektif industri terhadap masalah ini merupakan autentikasi multi-faktor, namun implementasinya terpecah-pecah dan banyak yang masih belum dapat menangani phishing secara memadai.

Web Authentication API, atau WebAuthn, adalah protokol tahan phishing standar yang dapat digunakan oleh aplikasi web apa pun.

Cara kerja

Sumber: webauthn.guide

WebAuthn memungkinkan server mendaftarkan dan mengautentikasi pengguna menggunakan kriptografi kunci publik, bukan sandi. Situs dapat membuat kredensial yang terdiri dari pasangan kunci pribadi-publik.

  • Kunci pribadi disimpan dengan aman di perangkat pengguna.
  • Kunci publik dan ID kredensial yang dibuat secara acak akan dikirim ke server untuk disimpan.

Kunci publik digunakan oleh server untuk membuktikan identitas pengguna. Hal ini tidak bersifat rahasia, karena tidak berguna tanpa kunci pribadi yang sesuai.

Manfaat

WebAuthn memiliki dua manfaat utama:

  • Tidak ada rahasia bersama: server tidak menyimpan rahasia. Hal ini membuat database kurang menarik bagi peretas, karena kunci publik tidak berguna bagi mereka.
  • Kredensial cakupan: kredensial yang terdaftar untuk
     <div class="flex-h-between">
        <h3>
            Two-factor authentication
        </h3>
        <button class="create" id="registerButton" raised>
            ➕ Add a credential
        </button>
    </div>
    
    3 tidak dapat digunakan di
     <div class="flex-h-between">
        <h3>
            Two-factor authentication
        </h3>
        <button class="create" id="registerButton" raised>
            ➕ Add a credential
        </button>
    </div>
    
    4. Tindakan ini membuat WebAuthn kebal phishing.

Kasus penggunaan

Salah satu kasus penggunaan untuk WebAuthn adalah autentikasi 2 langkah dengan kunci keamanan. Hal ini mungkin sangat relevan untuk aplikasi web perusahaan.

Dukungan browser

WebAuthn didukung di Chrome, Firefox, dan Edge, serta Safari.

WebAuthn ditulis oleh W3C dan FIDO, dengan partisipasi Google, Mozilla, Microsoft, Yubico, dan lainnya.

Glosarium

  • Pengautentikasi: entitas software atau hardware yang dapat mendaftarkan pengguna dan kemudian menegaskan kepemilikan kredensial yang terdaftar. Ada dua jenis pengautentikasi:
  • Pengautentikasi roaming: pengautentikasi yang dapat digunakan dengan setiap perangkat yang digunakan untuk login oleh pengguna. Contoh: kunci keamanan USB, smartphone.
  • Pengautentikasi platform: pengautentikasi yang terpasang di perangkat pengguna. Contoh: Touch ID Apple.
  • Kredensial: pasangan kunci pribadi-publik
  • Pihak pengandal: (server untuk) situs yang mencoba mengautentikasi pengguna
  • Server FIDO: server yang digunakan untuk autentikasi. FIDO adalah sekumpulan protokol yang dikembangkan oleh aliansi FIDO; salah satu protokol ini adalah WebAuthn.

Dalam workshop ini, kita akan menggunakan pengautentikasi roaming.

3. Sebelum memulai

Yang Anda perlukan

Untuk menyelesaikan codelab ini, Anda memerlukan:

  • Pemahaman dasar tentang WebAuthn.
  • Pengetahuan dasar tentang JavaScript dan HTML.
  • Browser terbaru yang mendukung WebAuthn.
  • Kunci keamanan yang sesuai dengan U2F.

Anda dapat menggunakan salah satu dari berikut ini sebagai kunci keamanan:

  • Ponsel Android dengan Android>=7 (Nougat) yang menjalankan Chrome. Dalam kasus ini, Anda juga memerlukan komputer Windows, macOS, atau ChromeOS dengan Bluetooth yang berfungsi.
  • Kunci USB, seperti YubiKey.

Untuk mendapatkan kunci keamanan Anda buka g co sc di jendela browser baru

Sumber: https://www.yubico.com/products/security-key/

Tidak ada kunci keamanan? Bukan masalah.

Jika tidak memiliki kunci keamanan, Anda dapat menggunakan Chrome DevTools untuk mengemulasi kunci keamanan.

Lihat caranya di Mengemulasi pengautentikasi dan men-debug WebAuthn.

Untuk mendapatkan kunci keamanan Anda buka g co sc di jendela browser baru

Yang akan Anda pelajari

Anda akan mempelajari ✅

  • Cara mendaftar dan menggunakan kunci keamanan sebagai faktor kedua untuk autentikasi WebAuthn.
  • Cara membuat proses ini mudah digunakan.

Anda tidak akan mempelajari ❌

  • Cara mem-build server FIDO—server yang digunakan untuk autentikasi. Ini tidak masalah karena biasanya, sebagai aplikasi web atau developer situs, Anda akan mengandalkan implementasi server FIDO yang sudah ada. Pastikan untuk selalu memverifikasi fungsi dan kualitas implementasi server yang Anda andalkan. Dalam codelab ini, server FIDO menggunakan SimpleWebAuthn. Untuk opsi lainnya, lihat halaman resmi FIDO Alliance. Untuk library open source, lihat webauthn.io atau AwesomeWebAuthn.

Pernyataan penyangkalan

Pengguna harus memasukkan sandi untuk login. Namun, untuk memudahkan codelab ini, sandi tidak disimpan atau diperiksa. Dalam aplikasi yang sebenarnya, Anda akan memeriksa apakah sisi server tersebut benar atau tidak.

Pemeriksaan keamanan dasar seperti pemeriksaan CSRF, validasi sesi, dan pembersihan input diimplementasikan dalam codelab ini. Namun, ada banyak langkah keamanan yang tidak diperlukan. Misalnya, tidak ada batasan input sandi untuk mencegah serangan brute force. Tidak masalah di sini karena sandi tidak disimpan, namun pastikan Anda tidak menggunakan kode ini apa adanya dalam produksi.

Perhatian: Kode yang ditampilkan dalam codelab ini adalah untuk tujuan pembelajaran. Jangan menggunakannya dalam produksi.

4. Menyiapkan pengautentikasi Anda

Jika Anda menggunakan ponsel Android sebagai pengautentikasi

  • Pastikan Chrome merupakan versi terbaru di desktop dan ponsel Anda.
  • Di desktop dan ponsel Anda, buka Chrome dan login dengan profil yang sama⏤profil yang ingin Anda gunakan untuk workshop ini.
  • Aktifkan Sinkronisasi untuk profil ini, di desktop dan ponsel Anda. Gunakan chrome://settings/syncSetup untuk ini.
  • Aktifkan Bluetooth di desktop dan ponsel Anda.
  • Di desktop Chrome tempat login dengan profil yang sama, buka webauthn.io.
  • Masukkan nama pengguna yang sederhana. Biarkan Jenis pengesahan dan Jenis pengautentikasi ke nilai Tidak ada dan Tidak ditentukan (default). Klik Daftar.

Untuk mendapatkan kunci keamanan Anda buka g co sc di jendela browser baru

  • Jendela browser akan terbuka, yang meminta Anda untuk memverifikasi identitas. Pilih ponsel Anda dalam daftar.

Untuk mendapatkan kunci keamanan Anda buka g co sc di jendela browser baru
Untuk mendapatkan kunci keamanan Anda buka g co sc di jendela browser baru

Perhatian: Windows mengimplementasikan banyak WebAuthn secara native, sehingga akan terlihat berbeda di Windows. Di macOS, Anda akan melihat UI mirip Chrome yang serupa dengan screenshot di atas.

  • Di ponsel, Anda akan mendapatkan notifikasi berjudul Verifikasi identitas Anda. Ketuk notifikasi tersebut.
  • Di ponsel, Anda akan dimintai kode PIN ponsel (atau menyentuh sensor sidik jari). Masukkan kode.
  • Di webauthn.io pada desktop, indikator "Berhasil" akan muncul.

Untuk mendapatkan kunci keamanan Anda buka g co sc di jendela browser baru

  • Di webauthn.io pada desktop, klik tombol Login.
  • Sekali lagi, jendela browser akan terbuka; pilih ponsel Anda dalam daftar.
  • Di ponsel, ketuk notifikasi yang muncul, dan masukkan PIN Anda (atau sentuh sensor sidik jari).
  • webauthn.io akan memberi tahu bahwa Anda telah login. Ponsel Anda berfungsi dengan semestinya sebagai kunci keamanan; Anda siap mengikuti workshop ini.

Jika Anda menggunakan kunci keamanan USB sebagai pengautentikasi

  • Di desktop Chrome, buka webauthn.io.
  • Masukkan nama pengguna yang sederhana. Biarkan Jenis pengesahan dan Jenis pengautentikasi ke nilai Tidak ada dan Tidak ditentukan (default). Klik Daftar.
  • Jendela browser akan terbuka, yang meminta Anda untuk memverifikasi identitas. Pilih Kunci keamanan USB dalam daftar.

Untuk mendapatkan kunci keamanan Anda buka g co sc di jendela browser baru
Untuk mendapatkan kunci keamanan Anda buka g co sc di jendela browser baru

  • Masukkan kunci keamanan Anda ke desktop dan sentuh kunci tersebut.

Untuk mendapatkan kunci keamanan Anda buka g co sc di jendela browser baru

  • Di webauthn.io pada desktop, indikator "Berhasil" akan muncul.

Untuk mendapatkan kunci keamanan Anda buka g co sc di jendela browser baru

  • Di webauthn.io pada desktop, klik tombol Login.
  • Sekali lagi, jendela browser akan terbuka; pilih Kunci keamanan USB dalam daftar.
  • Sentuh kunci.
  • Webauthn.io akan memberi tahu bahwa Anda telah login. Kunci keamanan USB Anda berfungsi dengan semestinya; Anda siap mengikuti workshop ini.

Untuk mendapatkan kunci keamanan Anda buka g co sc di jendela browser baru

5. Memulai persiapan

Dalam codelab ini, Anda akan menggunakan Glitch, editor kode online yang secara otomatis dan instan men-deploy kode Anda.

Membuat fork kode awal

Buka project awal.

Klik tombol Remix.

Tindakan ini akan membuat salinan kode awal. Kini, Anda memiliki kode sendiri untuk diedit. Fork (disebut "remix" di Glitch) adalah tempat Anda akan melakukan semua pekerjaan untuk codelab ini.

Untuk mendapatkan kunci keamanan Anda buka g co sc di jendela browser baru

Mempelajari kode awal

Pelajari kode awal yang baru saja Anda buat fork-nya.

Perhatikan bahwa di bagian

 <div class="flex-h-between">
    <h3>
        Two-factor authentication
    </h3>
    <button class="create" id="registerButton" raised>
        ➕ Add a credential
    </button>
</div>
5, library bernama
 <div class="flex-h-between">
    <h3>
        Two-factor authentication
    </h3>
    <button class="create" id="registerButton" raised>
        ➕ Add a credential
    </button>
</div>
6 sudah disediakan. Ini adalah library kustom yang menangani logika autentikasi sisi server. Library ini menggunakan library
 <div class="flex-h-between">
    <h3>
        Two-factor authentication
    </h3>
    <button class="create" id="registerButton" raised>
        ➕ Add a credential
    </button>
</div>
7 sebagai dependensi.

6. Mengimplementasikan pendaftaran kredensial

Kapan saja dalam codelab ini, Anda dapat melihat kode akhir (dan aplikasi web) sebagai referensi.

Mengimplementasikan pendaftaran kredensial

Hal pertama yang kita perlukan untuk menyiapkan autentikasi 2 langkah dengan kunci keamanan adalah memungkinkan pengguna membuat kredensial.

Mari kita tambahkan fungsi tersebut terlebih dahulu dalam kode sisi klien.

Dalam codelab ini, semua kode sisi klien terkait autentikasi akan berada di

 <div class="flex-h-between">
    <h3>
        Two-factor authentication
    </h3>
    <button class="create" id="registerButton" raised>
        ➕ Add a credential
    </button>
</div>
8.

Di

 <div class="flex-h-between">
    <h3>
        Two-factor authentication
    </h3>
    <button class="create" id="registerButton" raised>
        ➕ Add a credential
    </button>
</div>
8, perhatikan bahwa terdapat fungsi bernama
<div class="flex-h-between">
(HTML you've just added)
</div>
<div id="credentials"></div>
0 yang belum melakukan apa pun. Tambahkan kode berikut ke dalamnya:

async function registerCredential() {
  // Fetch the credential creation options from the backend
  const credentialCreationOptionsFromServer = await _fetch(
    "/auth/credential-options",
    "POST"
  );
  // Decode the credential creation options
  const credentialCreationOptions = decodeServerOptions(
    credentialCreationOptionsFromServer
  );
  // Create a credential via the browser API; this will prompt the user to touch their security key or tap a button on their phone
  const credential = await navigator.credentials.create({
    publicKey: {
      ...credentialCreationOptions,
    }
  });
  // Encode the newly created credential to send it to the backend
  const encodedCredential = encodeCredential(credential);
  // Send the encoded credential to the backend for storage
  return await _fetch("/auth/credential", "POST", encodedCredential);
}

Perhatikan bahwa fungsi ini sudah diekspor untuk Anda.

Berikut adalah hal yang dilakukan

<div class="flex-h-between">
(HTML you've just added)
</div>
<div id="credentials"></div>
1:

  • Fungsi ini mengambil opsi pembuatan kredensial dari server (
    <div class="flex-h-between">
    (HTML you've just added)
    </div>
    <div id="credentials"></div>
    
    2)
  • Karena opsi server kembali dienkode, opsi ini menggunakan fungsi utilitas
    <div class="flex-h-between">
    (HTML you've just added)
    </div>
    <div id="credentials"></div>
    
    3 untuk mendekodenya.
  • Fungsi ini membuat kredensial dengan memanggil API web
    <div class="flex-h-between">
    (HTML you've just added)
    </div>
    <div id="credentials"></div>
    
    4. Saat
    <div class="flex-h-between">
    (HTML you've just added)
    </div>
    <div id="credentials"></div>
    
    4 dipanggil, browser akan mengambil alih dan meminta pengguna memilih kunci keamanan.
  • Fungsi ini mendekode kredensial yang baru dibuat
  • Fungsi ini mendaftarkan kredensial sisi server baru dengan membuat permintaan ke
    <div class="flex-h-between">
    (HTML you've just added)
    </div>
    <div id="credentials"></div>
    
    6 yang berisi kredensial yang dienkode.

Nanti dalam tutorial ini, Anda akan mengedit

<div class="flex-h-between">
(HTML you've just added)
</div>
<div id="credentials"></div>
0 untuk memastikan kode Anda berjalan di semua browser dan memanfaatkan fitur WebAuthn yang menarik. Untuk saat ini, mari kita fokus pada fungsi dasar.

Tambahan lain: lihat kode server

Baca ini jika Anda ingin memahami berbagai konfigurasi autentikasi yang ditawarkan WebAuthn, dan cara penggunaannya di backend. Jika ini adalah pertama kalinya Anda menggunakan WebAuthn dan ingin memahami API, Anda juga dapat melewatinya untuk saat ini dan kembali lagi nanti.

<div class="flex-h-between">
(HTML you've just added)
</div>
<div id="credentials"></div>
0 melakukan dua panggilan ke server, jadi mari kita luangkan waktu sejenak untuk melihat apa yang terjadi di backend.

Opsi pembuatan kredensial

Saat klien membuat permintaan ke (

<div class="flex-h-between">
(HTML you've just added)
</div>
<div id="credentials"></div>
2), server akan membuat objek opsi dan mengirimkannya kembali ke klien.

Objek ini kemudian digunakan oleh klien dalam panggilan pembuatan kredensial yang sebenarnya:

navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}

Jadi, apa yang ada dalam

// Set up the handler for the button that registers credentials
const registerButton = document.querySelector('#registerButton');
registerButton.addEventListener('click', register);

// Register a credential
async function register() {
  let user = {};
  try {
    const user = await registerCredential();
  } catch (e) {
    // Alert the user that something went wrong
    if (Array.isArray(e)) {
      alert(
        // `msg` not `message`, this is the key's name as per the express validator API
        `Registration failed. ${e.map((err) => `${err.msg} (${err.param})`)}`
      );
    } else {
      alert(`Registration failed. ${e}`);
    }
  }
}
0 ini yang pada akhirnya digunakan dalam
<div class="flex-h-between">
(HTML you've just added)
</div>
<div id="credentials"></div>
1 sisi klien yang telah Anda terapkan pada langkah sebelumnya?

Lihat kode server di bagian router.post("/credential-options", ....

Mari kita tidak melihat setiap properti, tetapi berikut adalah beberapa properti menarik yang dapat Anda lihat di objek opsi kode server, yang dihasilkan menggunakan library

// Set up the handler for the button that registers credentials
const registerButton = document.querySelector('#registerButton');
registerButton.addEventListener('click', register);

// Register a credential
async function register() {
  let user = {};
  try {
    const user = await registerCredential();
  } catch (e) {
    // Alert the user that something went wrong
    if (Array.isArray(e)) {
      alert(
        // `msg` not `message`, this is the key's name as per the express validator API
        `Registration failed. ${e.map((err) => `${err.msg} (${err.param})`)}`
      );
    } else {
      alert(`Registration failed. ${e}`);
    }
  }
}
2 dan pada akhirnya dikembalikan ke klien:

  • // Set up the handler for the button that registers credentials
    const registerButton = document.querySelector('#registerButton');
    registerButton.addEventListener('click', register);
    
    // Register a credential
    async function register() {
      let user = {};
      try {
        const user = await registerCredential();
      } catch (e) {
        // Alert the user that something went wrong
        if (Array.isArray(e)) {
          alert(
            // `msg` not `message`, this is the key's name as per the express validator API
            `Registration failed. ${e.map((err) => `${err.msg} (${err.param})`)}`
          );
        } else {
          alert(`Registration failed. ${e}`);
        }
      }
    }
    
    3 dan
    // Set up the handler for the button that registers credentials
    const registerButton = document.querySelector('#registerButton');
    registerButton.addEventListener('click', register);
    
    // Register a credential
    async function register() {
      let user = {};
      try {
        const user = await registerCredential();
      } catch (e) {
        // Alert the user that something went wrong
        if (Array.isArray(e)) {
          alert(
            // `msg` not `message`, this is the key's name as per the express validator API
            `Registration failed. ${e.map((err) => `${err.msg} (${err.param})`)}`
          );
        } else {
          alert(`Registration failed. ${e}`);
        }
      }
    }
    
    4 mendeskripsikan organisasi yang mendaftarkan dan mengautentikasi pengguna. Perlu diketahui bahwa di WebAuthn, kredensial dicakup untuk domain tertentu, yang merupakan manfaat keamanan;
    // Set up the handler for the button that registers credentials
    const registerButton = document.querySelector('#registerButton');
    registerButton.addEventListener('click', register);
    
    // Register a credential
    async function register() {
      let user = {};
      try {
        const user = await registerCredential();
      } catch (e) {
        // Alert the user that something went wrong
        if (Array.isArray(e)) {
          alert(
            // `msg` not `message`, this is the key's name as per the express validator API
            `Registration failed. ${e.map((err) => `${err.msg} (${err.param})`)}`
          );
        } else {
          alert(`Registration failed. ${e}`);
        }
      }
    }
    
    3 dan
    // Set up the handler for the button that registers credentials
    const registerButton = document.querySelector('#registerButton');
    registerButton.addEventListener('click', register);
    
    // Register a credential
    async function register() {
      let user = {};
      try {
        const user = await registerCredential();
      } catch (e) {
        // Alert the user that something went wrong
        if (Array.isArray(e)) {
          alert(
            // `msg` not `message`, this is the key's name as per the express validator API
            `Registration failed. ${e.map((err) => `${err.msg} (${err.param})`)}`
          );
        } else {
          alert(`Registration failed. ${e}`);
        }
      }
    }
    
    4 di sini digunakan untuk menentukan cakupan kredensial.
    // Set up the handler for the button that registers credentials
    const registerButton = document.querySelector('#registerButton');
    registerButton.addEventListener('click', register);
    
    // Register a credential
    async function register() {
      let user = {};
      try {
        const user = await registerCredential();
      } catch (e) {
        // Alert the user that something went wrong
        if (Array.isArray(e)) {
          alert(
            // `msg` not `message`, this is the key's name as per the express validator API
            `Registration failed. ${e.map((err) => `${err.msg} (${err.param})`)}`
          );
        } else {
          alert(`Registration failed. ${e}`);
        }
      }
    }
    
    4 yang valid, misalnya, nama host situs Anda. Perhatikan bagaimana hal ini akan secara otomatis diperbarui saat Anda membuat fork ke project awal 🧘🏻‍♀️
  • // Set up the handler for the button that registers credentials
    const registerButton = document.querySelector('#registerButton');
    registerButton.addEventListener('click', register);
    
    // Register a credential
    async function register() {
      let user = {};
      try {
        const user = await registerCredential();
      } catch (e) {
        // Alert the user that something went wrong
        if (Array.isArray(e)) {
          alert(
            // `msg` not `message`, this is the key's name as per the express validator API
            `Registration failed. ${e.map((err) => `${err.msg} (${err.param})`)}`
          );
        } else {
          alert(`Registration failed. ${e}`);
        }
      }
    }
    
    8 adalah daftar kredensial; kredensial baru tidak dapat dibuat di pengautentikasi yang juga berisi salah satu kredensial yang tercantum di
    // Set up the handler for the button that registers credentials
    const registerButton = document.querySelector('#registerButton');
    registerButton.addEventListener('click', register);
    
    // Register a credential
    async function register() {
      let user = {};
      try {
        const user = await registerCredential();
      } catch (e) {
        // Alert the user that something went wrong
        if (Array.isArray(e)) {
          alert(
            // `msg` not `message`, this is the key's name as per the express validator API
            `Registration failed. ${e.map((err) => `${err.msg} (${err.param})`)}`
          );
        } else {
          alert(`Registration failed. ${e}`);
        }
      }
    }
    
    8. Dalam codelab kita,
    // Set up the handler for the button that registers credentials
    const registerButton = document.querySelector('#registerButton');
    registerButton.addEventListener('click', register);
    
    // Register a credential
    async function register() {
      let user = {};
      try {
        const user = await registerCredential();
      } catch (e) {
        // Alert the user that something went wrong
        if (Array.isArray(e)) {
          alert(
            // `msg` not `message`, this is the key's name as per the express validator API
            `Registration failed. ${e.map((err) => `${err.msg} (${err.param})`)}`
          );
        } else {
          alert(`Registration failed. ${e}`);
        }
      }
    }
    
    8 adalah daftar kredensial yang ada untuk pengguna ini. Dengan ini dan
    // Update the list that displays credentials
    async function updateCredentialList() {
      // Fetch the latest credential list from the backend
      const response = await _fetch('/auth/credentials', 'GET');
      const credentials = response.credentials || [];
      // Generate the credential list as HTML and pass remove/rename functions as args
      const credentialListHtml = getCredentialListHtml(
        credentials,
        removeEl,
        renameEl
      );
      // Display the list of credentials in the DOM
      const list = document.querySelector('#credentials');
      render(credentialListHtml, list);
    }
    
    1, kita memastikan bahwa setiap kredensial yang dibuat pengguna akan berada di pengautentikasi yang berbeda (kunci keamanan). Ini adalah praktik yang baik karena artinya jika pengguna telah mendaftarkan beberapa kredensial, mereka akan menggunakan pengautentikasi yang berbeda (kunci keamanan), jadi kehilangan satu kunci keamanan tidak akan membuat pengguna terkunci dari akun mereka.
  • // Update the list that displays credentials
    async function updateCredentialList() {
      // Fetch the latest credential list from the backend
      const response = await _fetch('/auth/credentials', 'GET');
      const credentials = response.credentials || [];
      // Generate the credential list as HTML and pass remove/rename functions as args
      const credentialListHtml = getCredentialListHtml(
        credentials,
        removeEl,
        renameEl
      );
      // Display the list of credentials in the DOM
      const list = document.querySelector('#credentials');
      render(credentialListHtml, list);
    }
    
    2 menentukan jenis pengautentikasi yang ingin Anda izinkan di aplikasi web. Mari kita pelajari lebih lanjut
    // Update the list that displays credentials
    async function updateCredentialList() {
      // Fetch the latest credential list from the backend
      const response = await _fetch('/auth/credentials', 'GET');
      const credentials = response.credentials || [];
      // Generate the credential list as HTML and pass remove/rename functions as args
      const credentialListHtml = getCredentialListHtml(
        credentials,
        removeEl,
        renameEl
      );
      // Display the list of credentials in the DOM
      const list = document.querySelector('#credentials');
      render(credentialListHtml, list);
    }
    
    2:
    • // Update the list that displays credentials
      async function updateCredentialList() {
        // Fetch the latest credential list from the backend
        const response = await _fetch('/auth/credentials', 'GET');
        const credentials = response.credentials || [];
        // Generate the credential list as HTML and pass remove/rename functions as args
        const credentialListHtml = getCredentialListHtml(
          credentials,
          removeEl,
          renameEl
        );
        // Display the list of credentials in the DOM
        const list = document.querySelector('#credentials');
        render(credentialListHtml, list);
      }
      
      4 berarti aplikasi ini tidak menerapkan kredensial yang dapat ditemukan sisi klien. Kredensial yang dapat ditemukan sisi klien adalah jenis kredensial khusus yang memungkinkan autentikasi pengguna tanpa perlu mengidentifikasinya terlebih dahulu. Di sini, kita telah menyiapkan
      // Update the list that displays credentials
      async function updateCredentialList() {
        // Fetch the latest credential list from the backend
        const response = await _fetch('/auth/credentials', 'GET');
        const credentials = response.credentials || [];
        // Generate the credential list as HTML and pass remove/rename functions as args
        const credentialListHtml = getCredentialListHtml(
          credentials,
          removeEl,
          renameEl
        );
        // Display the list of credentials in the DOM
        const list = document.querySelector('#credentials');
        render(credentialListHtml, list);
      }
      
      5 karena codelab ini berfokus pada implementasi dasar; kredensial yang dapat ditemukan adalah untuk alur yang lebih canggih.
    • // Update the list that displays credentials
      async function updateCredentialList() {
        // Fetch the latest credential list from the backend
        const response = await _fetch('/auth/credentials', 'GET');
        const credentials = response.credentials || [];
        // Generate the credential list as HTML and pass remove/rename functions as args
        const credentialListHtml = getCredentialListHtml(
          credentials,
          removeEl,
          renameEl
        );
        // Display the list of credentials in the DOM
        const list = document.querySelector('#credentials');
        render(credentialListHtml, list);
      }
      
      6 hanya ada untuk kompatibilitas mundur dengan WebAuthn v1.
    • // Update the list that displays credentials
      async function updateCredentialList() {
        // Fetch the latest credential list from the backend
        const response = await _fetch('/auth/credentials', 'GET');
        const credentials = response.credentials || [];
        // Generate the credential list as HTML and pass remove/rename functions as args
        const credentialListHtml = getCredentialListHtml(
          credentials,
          removeEl,
          renameEl
        );
        // Display the list of credentials in the DOM
        const list = document.querySelector('#credentials');
        render(credentialListHtml, list);
      }
      
      7 berarti bahwa jika pengautentikasi mendukung verifikasi pengguna—misalnya, jika itu adalah kunci keamanan biometrik atau kunci dengan fitur PIN bawaan—pihak pengandal akan memintanya saat membuat kredensial data. Jika pengautentikasi tidak melakukannya—kunci keamanan dasar—, server tidak akan meminta verifikasi pengguna.
  • // Update the list that displays credentials
    async function updateCredentialList() {
      // Fetch the latest credential list from the backend
      const response = await _fetch('/auth/credentials', 'GET');
      const credentials = response.credentials || [];
      // Generate the credential list as HTML and pass remove/rename functions as args
      const credentialListHtml = getCredentialListHtml(
        credentials,
        removeEl,
        renameEl
      );
      // Display the list of credentials in the DOM
      const list = document.querySelector('#credentials');
      render(credentialListHtml, list);
    }
    
    8 menjelaskan properti kriptografi kredensial pilihan berdasarkan urutan preferensi.

Semua opsi ini adalah keputusan yang harus dibuat oleh aplikasi web untuk model keamanannya. Amati bahwa pada server, opsi ini ditentukan dalam satu objek

// Update the list that displays credentials
async function updateCredentialList() {
  // Fetch the latest credential list from the backend
  const response = await _fetch('/auth/credentials', 'GET');
  const credentials = response.credentials || [];
  // Generate the credential list as HTML and pass remove/rename functions as args
  const credentialListHtml = getCredentialListHtml(
    credentials,
    removeEl,
    renameEl
  );
  // Display the list of credentials in the DOM
  const list = document.querySelector('#credentials');
  render(credentialListHtml, list);
}
9.

Verifikasi login

Bagian lainnya yang lebih menarik di sini adalah

<script type="module">
    // ... (imports)
    // Initialize the credential list by updating it once on page load
    updateCredentialList();
0.

Karena WebAuthn adalah protokol kriptografi, WebAuthn bergantung pada verifikasi login acak untuk menghindari serangan replay—saat penyerang mencuri payload untuk melakukan replay autentikasi, padahal ia bukan pemilik kunci pribadi yang akan mengaktifkan autentikasi.

Untuk mengurangi hal ini, verifikasi login dibuat di server, dan akan ditandatangani dengan cepat; tanda tangan kemudian akan dibandingkan dengan yang diharapkan. Tindakan ini memverifikasi bahwa pengguna menyimpan kunci pribadi pada saat pembuatan kredensial.

Kode pendaftaran kredensial

Lihat kode server di bagian router.post("/credential", ....

Di sinilah kredensial didaftarkan sisi server.

Jadi, apa yang terjadi?

Salah satu bit yang paling penting dalam kode ini adalah panggilan terverifikasi, melalui

<script type="module">
    // ... (imports)
    // Initialize the credential list by updating it once on page load
    updateCredentialList();
1:

  • Tantangan yang ditandatangani akan diperiksa, dan hal ini memastikan bahwa kredensial dibuat oleh seseorang yang benar-benar menyimpan kunci pribadi pada saat pembuatan.
  • ID pihak pengikat, yang terikat dengan asalnya, juga diverifikasi. Hal ini memastikan bahwa kredensial terikat ke aplikasi web ini (dan hanya aplikasi web ini).

Menambahkan fungsi ini ke UI

Setelah fungsi Anda membuat kredensial, ``registerCredential()

<script type="module">
    // ... (imports)
    // Initialize the credential list by updating it once on page load
    updateCredentialList();
2sudah siap, mari kita sediakan kredensial tersebut untuk pengguna.

Anda akan melakukannya dari halaman Akun, karena ini adalah lokasi standar untuk pengelolaan autentikasi.

Pada markup

<script type="module">
    // ... (imports)
    // Initialize the credential list by updating it once on page load
    updateCredentialList();
3, di bawah nama pengguna, ada
<script type="module">
    // ... (imports)
    // Initialize the credential list by updating it once on page load
    updateCredentialList();
4 yang masih kosong dengan class tata letak
<script type="module">
    // ... (imports)
    // Initialize the credential list by updating it once on page load
    updateCredentialList();
5. Kita akan menggunakan
<script type="module">
    // ... (imports)
    // Initialize the credential list by updating it once on page load
    updateCredentialList();
4 ini untuk elemen UI yang terkait dengan fungsi 2FA.

Tambahkan ke div ini:

  • Judul yang bertuliskan "Autentikasi 2 langkah"
  • Tombol untuk membuat kredensial
 <div class="flex-h-between">
    <h3>
        Two-factor authentication
    </h3>
    <button class="create" id="registerButton" raised>
        ➕ Add a credential
    </button>
</div>

Di bawah div ini, tambahkan div kredensial yang akan kita perlukan nanti:

<div class="flex-h-between">
(HTML you've just added)
</div>
<div id="credentials"></div>

Di skrip inline

<script type="module">
    // ... (imports)
    // Initialize the credential list by updating it once on page load
    updateCredentialList();
3, impor fungsi yang baru saja Anda buat dan tambahkan fungsi
<script type="module">
    // ... (imports)
    // Initialize the credential list by updating it once on page load
    updateCredentialList();
8 yang memanggilnya, serta pengendali peristiwa yang dipasangkan ke tombol yang baru saja Anda buat.

// Set up the handler for the button that registers credentials
const registerButton = document.querySelector('#registerButton');
registerButton.addEventListener('click', register);

// Register a credential
async function register() {
  let user = {};
  try {
    const user = await registerCredential();
  } catch (e) {
    // Alert the user that something went wrong
    if (Array.isArray(e)) {
      alert(
        // `msg` not `message`, this is the key's name as per the express validator API
        `Registration failed. ${e.map((err) => `${err.msg} (${err.param})`)}`
      );
    } else {
      alert(`Registration failed. ${e}`);
    }
  }
}

Tampilkan kredensial yang dapat dilihat pengguna

Setelah Anda menambahkan fungsi untuk membuat kredensial, pengguna memerlukan cara untuk melihat kredensial yang telah mereka tambahkan.

Halaman Akun adalah tempat yang tepat untuk hal ini.

Di

<script type="module">
    // ... (imports)
    // Initialize the credential list by updating it once on page load
    updateCredentialList();
3, cari fungsi bernama
async function register() {
  let user = {};
  try {
    // ...
  } catch (e) {
    // ...
  }
  // Refresh the credential list to display the new credential
  await updateCredentialList();
}
0.

Tambahkan kode berikut yang membuat panggilan backend untuk mengambil semua kredensial terdaftar untuk pengguna yang saat ini login, dan yang menampilkan kredensial yang ditampilkan:

// Update the list that displays credentials
async function updateCredentialList() {
  // Fetch the latest credential list from the backend
  const response = await _fetch('/auth/credentials', 'GET');
  const credentials = response.credentials || [];
  // Generate the credential list as HTML and pass remove/rename functions as args
  const credentialListHtml = getCredentialListHtml(
    credentials,
    removeEl,
    renameEl
  );
  // Display the list of credentials in the DOM
  const list = document.querySelector('#credentials');
  render(credentialListHtml, list);
}

Untuk saat ini, jangan pikirkan tentang

async function register() {
  let user = {};
  try {
    // ...
  } catch (e) {
    // ...
  }
  // Refresh the credential list to display the new credential
  await updateCredentialList();
}
1 dan
async function register() {
  let user = {};
  try {
    // ...
  } catch (e) {
    // ...
  }
  // Refresh the credential list to display the new credential
  await updateCredentialList();
}
2; Anda akan mempelajarinya nanti dalam codelab ini.

Tambahkan satu panggilan ke

async function register() {
  let user = {};
  try {
    // ...
  } catch (e) {
    // ...
  }
  // Refresh the credential list to display the new credential
  await updateCredentialList();
}
3 di awal skrip inline, dalam
<script type="module">
    // ... (imports)
    // Initialize the credential list by updating it once on page load
    updateCredentialList();
3. Dengan panggilan ini, kredensial yang tersedia diambil saat pengguna membuka halaman akunnya.

<script type="module">
    // ... (imports)
    // Initialize the credential list by updating it once on page load
    updateCredentialList();

Sekarang, panggil

async function register() {
  let user = {};
  try {
    // ...
  } catch (e) {
    // ...
  }
  // Refresh the credential list to display the new credential
  await updateCredentialList();
}
3 setelah
<div class="flex-h-between">
(HTML you've just added)
</div>
<div id="credentials"></div>
1 berhasil diselesaikan, sehingga daftar menampilkan kredensial yang baru dibuat:

async function register() {
  let user = {};
  try {
    // ...
  } catch (e) {
    // ...
  }
  // Refresh the credential list to display the new credential
  await updateCredentialList();
}

Cobalah! 👩🏻‍💻

Anda sudah menyelesaikan pendaftaran kredensial. Pengguna kini dapat membuat kredensial berbasis kunci keamanan, dan memvisualisasikannya di halaman Akun.

Coba:

  • Logout.
  • Login—dengan pengguna dan sandi apa pun. Seperti yang disebutkan sebelumnya, sandi ini tidak benar-benar diperiksa keakuratannya agar codelab ini tetap sederhana. Sandi tidak boleh kosong.
  • Setelah berada di halaman Akun, klik Tambahkan kredensial.
  • Anda akan diminta memasukkan dan menyentuh kunci keamanan. Lakukan.
  • Setelah berhasil membuat kredensial, kredensial akan ditampilkan di halaman akun.
  • Muat ulang halaman Akun. Kredensial akan ditampilkan.
  • Jika Anda memiliki dua kunci yang tersedia, coba tambahkan dua kunci keamanan yang berbeda sebagai kredensial. Keduanya seharusnya ditampilkan.
  • Cobalah membuat dua kredensial dengan pengautentikasi yang sama (kunci); Anda akan melihat bahwa opsi tersebut tidak didukung. Hal ini memang disengaja—hal ini dikarenakan penggunaan
    // Set up the handler for the button that registers credentials
    const registerButton = document.querySelector('#registerButton');
    registerButton.addEventListener('click', register);
    
    // Register a credential
    async function register() {
      let user = {};
      try {
        const user = await registerCredential();
      } catch (e) {
        // Alert the user that something went wrong
        if (Array.isArray(e)) {
          alert(
            // `msg` not `message`, this is the key's name as per the express validator API
            `Registration failed. ${e.map((err) => `${err.msg} (${err.param})`)}`
          );
        } else {
          alert(`Registration failed. ${e}`);
        }
      }
    }
    
    8 di backend kita.

Menghapus kredensial

Anda akan melihat bahwa kita telah mengimplementasikan fungsi untuk menghapus kredensial, dan menambahkannya ke kode awal. Dapat menghapus kredensial berguna untuk eksperimen cepat, misalnya dalam codelab ini; inilah alasan kami menambahkannya untuk Anda.

Anda juga ingin mendukung penghapusan kredensial dalam aplikasi yang sebenarnya; pengguna akan membutuhkannya jika mereka kehilangan salah satu kunci keamanan atau tidak ingin menggunakan kunci tertentu lagi.

7. Mengaktifkan autentikasi faktor kedua

Kapan saja dalam codelab ini, Anda dapat melihat kode akhir (dan aplikasi web) sebagai referensi.

Pengguna dapat mendaftarkan dan membatalkan pendaftaran kredensial, tetapi kredensial hanya ditampilkan dan belum digunakan.

Sekarang saatnya menggunakannya, dan siapkan autentikasi 2 langkah yang sebenarnya.

Di bagian ini, Anda akan mengubah alur autentikasi di aplikasi web Anda dari alur dasar ini:

Untuk mendapatkan kunci keamanan Anda buka g co sc di jendela browser baru

Untuk alur dua langkah ini:

Untuk mendapatkan kunci keamanan Anda buka g co sc di jendela browser baru

Mengimplementasikan autentikasi faktor kedua

Pertama, mari kita tambahkan fungsi yang diperlukan dan implementasikan komunikasi dengan backend; kita akan menambahkannya di frontend pada langkah berikutnya.

Hal yang perlu Anda terapkan di sini adalah fungsi yang mengautentikasi pengguna dengan kredensial.

Di

 <div class="flex-h-between">
    <h3>
        Two-factor authentication
    </h3>
    <button class="create" id="registerButton" raised>
        ➕ Add a credential
    </button>
</div>
8, cari fungsi kosong
async function register() {
  let user = {};
  try {
    // ...
  } catch (e) {
    // ...
  }
  // Refresh the credential list to display the new credential
  await updateCredentialList();
}
9, lalu tambahkan kode berikut:

async function authenticateTwoFactor() {
  // Fetch the 2F options from the backend
  const optionsFromServer = await _fetch("/auth/two-factor-options", "POST");
  // Decode them
  const decodedOptions = decodeServerOptions(optionsFromServer);
  // Get a credential via the browser API; this will prompt the user to touch their security key or tap a button on their phone
  const credential = await navigator.credentials.get({
    publicKey: decodedOptions
  });
  // Encode the credential
  const encodedCredential = encodeCredential(credential);
  // Send it to the backend for verification
  return await _fetch("/auth/authenticate-two-factor", "POST", {
    credential: encodedCredential
  });
}

Perhatikan bahwa fungsi ini sudah diekspor untuk Anda; kita akan membutuhkannya di langkah berikutnya.

Berikut adalah hal yang dilakukan

async function register() {
  let user = {};
  try {
    // ...
  } catch (e) {
    // ...
  }
  // Refresh the credential list to display the new credential
  await updateCredentialList();
}
9:

  • Fungsi ini meminta opsi autentikasi 2 langkah dari server. Sama seperti opsi pembuatan kredensial yang telah Anda lihat sebelumnya, opsi ini ditentukan di server dan bergantung pada model keamanan aplikasi web. Pelajari kode server di bagian
    async function authenticateTwoFactor() {
      // Fetch the 2F options from the backend
      const optionsFromServer = await _fetch("/auth/two-factor-options", "POST");
      // Decode them
      const decodedOptions = decodeServerOptions(optionsFromServer);
      // Get a credential via the browser API; this will prompt the user to touch their security key or tap a button on their phone
      const credential = await navigator.credentials.get({
        publicKey: decodedOptions
      });
      // Encode the credential
      const encodedCredential = encodeCredential(credential);
      // Send it to the backend for verification
      return await _fetch("/auth/authenticate-two-factor", "POST", {
        credential: encodedCredential
      });
    }
    
    1 untuk mengetahui detailnya.
  • Dengan memanggil
    async function authenticateTwoFactor() {
      // Fetch the 2F options from the backend
      const optionsFromServer = await _fetch("/auth/two-factor-options", "POST");
      // Decode them
      const decodedOptions = decodeServerOptions(optionsFromServer);
      // Get a credential via the browser API; this will prompt the user to touch their security key or tap a button on their phone
      const credential = await navigator.credentials.get({
        publicKey: decodedOptions
      });
      // Encode the credential
      const encodedCredential = encodeCredential(credential);
      // Send it to the backend for verification
      return await _fetch("/auth/authenticate-two-factor", "POST", {
        credential: encodedCredential
      });
    }
    
    2, tindakan ini memungkinkan browser mengambil alih dan meminta pengguna memasukkan dan menyentuh kunci yang terdaftar sebelumnya. Hal ini membuat kredensial dipilih khusus untuk operasi autentikasi faktor kedua ini.
  • Kredensial yang dipilih kemudian akan diteruskan dalam permintaan backend untuk mengambil("/auth/authenticate-two-factor"`. Jika kredensial tersebut valid untuk pengguna tersebut, pengguna akan diautentikasi.

Tambahan lain: lihat kode server

Perhatikan bahwa

async function authenticateTwoFactor() {
  // Fetch the 2F options from the backend
  const optionsFromServer = await _fetch("/auth/two-factor-options", "POST");
  // Decode them
  const decodedOptions = decodeServerOptions(optionsFromServer);
  // Get a credential via the browser API; this will prompt the user to touch their security key or tap a button on their phone
  const credential = await navigator.credentials.get({
    publicKey: decodedOptions
  });
  // Encode the credential
  const encodedCredential = encodeCredential(credential);
  // Send it to the backend for verification
  return await _fetch("/auth/authenticate-two-factor", "POST", {
    credential: encodedCredential
  });
}
3 telah menangani beberapa navigasi dan akses: hal ini memastikan bahwa halaman Akun hanya dapat diakses oleh pengguna yang diautentikasi, dan melakukan beberapa pengalihan yang diperlukan.

Sekarang, lihat kode server di bagian

async function authenticateTwoFactor() {
  // Fetch the 2F options from the backend
  const optionsFromServer = await _fetch("/auth/two-factor-options", "POST");
  // Decode them
  const decodedOptions = decodeServerOptions(optionsFromServer);
  // Get a credential via the browser API; this will prompt the user to touch their security key or tap a button on their phone
  const credential = await navigator.credentials.get({
    publicKey: decodedOptions
  });
  // Encode the credential
  const encodedCredential = encodeCredential(credential);
  // Send it to the backend for verification
  return await _fetch("/auth/authenticate-two-factor", "POST", {
    credential: encodedCredential
  });
}
4.

Ada dua hal menarik yang perlu diperhatikan di sini:

  • Sandi dan kredensial diperiksa secara bersamaan pada tahap ini. Ini adalah tindakan pengamanan: bagi pengguna yang telah menyiapkan autentikasi 2 langkah, kita tidak ingin alur UI terlihat berbeda bergantung pada apakah sandi benar atau tidak. Jadi, kita memeriksa sandi dan kredensial secara bersamaan di langkah ini.
  • Jika sandi dan kredensial valid, kita kemudian menyelesaikan autentikasi dengan memanggil
    async function authenticateTwoFactor() {
      // Fetch the 2F options from the backend
      const optionsFromServer = await _fetch("/auth/two-factor-options", "POST");
      // Decode them
      const decodedOptions = decodeServerOptions(optionsFromServer);
      // Get a credential via the browser API; this will prompt the user to touch their security key or tap a button on their phone
      const credential = await navigator.credentials.get({
        publicKey: decodedOptions
      });
      // Encode the credential
      const encodedCredential = encodeCredential(credential);
      // Send it to the backend for verification
      return await _fetch("/auth/authenticate-two-factor", "POST", {
        credential: encodedCredential
      });
    }
    
    5 Artinya, dalam praktiknya kita beralih sesi, dari sesi
    async function authenticateTwoFactor() {
      // Fetch the 2F options from the backend
      const optionsFromServer = await _fetch("/auth/two-factor-options", "POST");
      // Decode them
      const decodedOptions = decodeServerOptions(optionsFromServer);
      // Get a credential via the browser API; this will prompt the user to touch their security key or tap a button on their phone
      const credential = await navigator.credentials.get({
        publicKey: decodedOptions
      });
      // Encode the credential
      const encodedCredential = encodeCredential(credential);
      // Send it to the backend for verification
      return await _fetch("/auth/authenticate-two-factor", "POST", {
        credential: encodedCredential
      });
    }
    
    6 sementara ketika pengguna belum diautentikasi, ke sesi utama
    async function authenticateTwoFactor() {
      // Fetch the 2F options from the backend
      const optionsFromServer = await _fetch("/auth/two-factor-options", "POST");
      // Decode them
      const decodedOptions = decodeServerOptions(optionsFromServer);
      // Get a credential via the browser API; this will prompt the user to touch their security key or tap a button on their phone
      const credential = await navigator.credentials.get({
        publicKey: decodedOptions
      });
      // Encode the credential
      const encodedCredential = encodeCredential(credential);
      // Send it to the backend for verification
      return await _fetch("/auth/authenticate-two-factor", "POST", {
        credential: encodedCredential
      });
    }
    
    7 tempat pengguna diautentikasi.

Sertakan halaman autentikasi faktor kedua di alur penggunaan

Di folder

async function authenticateTwoFactor() {
  // Fetch the 2F options from the backend
  const optionsFromServer = await _fetch("/auth/two-factor-options", "POST");
  // Decode them
  const decodedOptions = decodeServerOptions(optionsFromServer);
  // Get a credential via the browser API; this will prompt the user to touch their security key or tap a button on their phone
  const credential = await navigator.credentials.get({
    publicKey: decodedOptions
  });
  // Encode the credential
  const encodedCredential = encodeCredential(credential);
  // Send it to the backend for verification
  return await _fetch("/auth/authenticate-two-factor", "POST", {
    credential: encodedCredential
  });
}
8, perhatikan halaman baru
async function authenticateTwoFactor() {
  // Fetch the 2F options from the backend
  const optionsFromServer = await _fetch("/auth/two-factor-options", "POST");
  // Decode them
  const decodedOptions = decodeServerOptions(optionsFromServer);
  // Get a credential via the browser API; this will prompt the user to touch their security key or tap a button on their phone
  const credential = await navigator.credentials.get({
    publicKey: decodedOptions
  });
  // Encode the credential
  const encodedCredential = encodeCredential(credential);
  // Send it to the backend for verification
  return await _fetch("/auth/authenticate-two-factor", "POST", {
    credential: encodedCredential
  });
}
9.

Tombol ini bertuliskan Gunakan kunci keamanan, tetapi untuk saat ini tombol tersebut tidak melakukan apa pun.

Buat tombol ini memanggil

    <main>
...
    </main>
    <script type="module">
      import { authenticateTwoFactor, authStatuses } from "/auth.client.js";

      const button = document.querySelector("#authenticateButton");
      button.addEventListener("click", async e => {
        try {
          // Ask the user to authenticate with the second factor; this will trigger a browser prompt
          const response = await authenticateTwoFactor();
          const { authStatus } = response;
          if (authStatus === authStatuses.COMPLETE) {
            // The user is properly authenticated => Navigate to the Account page
            location.href = "/account";
          } else {
            throw new Error("Two-factor authentication failed");
          }
        } catch (e) {
          // Alert the user that something went wrong
          alert(`Two-factor authentication failed. ${e}`);
        }
      });
    </script>
  </body>
</html>
0 saat diklik.

  • Jika
        <main>
    ...
        </main>
        <script type="module">
          import { authenticateTwoFactor, authStatuses } from "/auth.client.js";
    
          const button = document.querySelector("#authenticateButton");
          button.addEventListener("click", async e => {
            try {
              // Ask the user to authenticate with the second factor; this will trigger a browser prompt
              const response = await authenticateTwoFactor();
              const { authStatus } = response;
              if (authStatus === authStatuses.COMPLETE) {
                // The user is properly authenticated => Navigate to the Account page
                location.href = "/account";
              } else {
                throw new Error("Two-factor authentication failed");
              }
            } catch (e) {
              // Alert the user that something went wrong
              alert(`Two-factor authentication failed. ${e}`);
            }
          });
        </script>
      </body>
    </html>
    
    0 berhasil, alihkan pengguna ke halaman Akun mereka.
  • Jika tidak berhasil, beri tahu pengguna bahwa terjadi error. Dalam aplikasi yang sebenarnya, Anda akan mengimplementasikan pesan error yang lebih membantu— agar demo ini sederhana, kita hanya akan menggunakan peringatan jendela.
    <main>
...
    </main>
    <script type="module">
      import { authenticateTwoFactor, authStatuses } from "/auth.client.js";

      const button = document.querySelector("#authenticateButton");
      button.addEventListener("click", async e => {
        try {
          // Ask the user to authenticate with the second factor; this will trigger a browser prompt
          const response = await authenticateTwoFactor();
          const { authStatus } = response;
          if (authStatus === authStatuses.COMPLETE) {
            // The user is properly authenticated => Navigate to the Account page
            location.href = "/account";
          } else {
            throw new Error("Two-factor authentication failed");
          }
        } catch (e) {
          // Alert the user that something went wrong
          alert(`Two-factor authentication failed. ${e}`);
        }
      });
    </script>
  </body>
</html>

Menggunakan autentikasi faktor kedua

Sekarang Anda sudah siap untuk menambahkan langkah autentikasi faktor kedua.

Yang perlu Anda lakukan sekarang adalah menambahkan langkah ini dari

    <main>
...
    </main>
    <script type="module">
      import { authenticateTwoFactor, authStatuses } from "/auth.client.js";

      const button = document.querySelector("#authenticateButton");
      button.addEventListener("click", async e => {
        try {
          // Ask the user to authenticate with the second factor; this will trigger a browser prompt
          const response = await authenticateTwoFactor();
          const { authStatus } = response;
          if (authStatus === authStatuses.COMPLETE) {
            // The user is properly authenticated => Navigate to the Account page
            location.href = "/account";
          } else {
            throw new Error("Two-factor authentication failed");
          }
        } catch (e) {
          // Alert the user that something went wrong
          alert(`Two-factor authentication failed. ${e}`);
        }
      });
    </script>
  </body>
</html>
2, untuk pengguna yang telah mengonfigurasi autentikasi 2 langkah.

Untuk mendapatkan kunci keamanan Anda buka g co sc di jendela browser baru

Pada

    <main>
...
    </main>
    <script type="module">
      import { authenticateTwoFactor, authStatuses } from "/auth.client.js";

      const button = document.querySelector("#authenticateButton");
      button.addEventListener("click", async e => {
        try {
          // Ask the user to authenticate with the second factor; this will trigger a browser prompt
          const response = await authenticateTwoFactor();
          const { authStatus } = response;
          if (authStatus === authStatuses.COMPLETE) {
            // The user is properly authenticated => Navigate to the Account page
            location.href = "/account";
          } else {
            throw new Error("Two-factor authentication failed");
          }
        } catch (e) {
          // Alert the user that something went wrong
          alert(`Two-factor authentication failed. ${e}`);
        }
      });
    </script>
  </body>
</html>
2, di bagian
    <main>
...
    </main>
    <script type="module">
      import { authenticateTwoFactor, authStatuses } from "/auth.client.js";

      const button = document.querySelector("#authenticateButton");
      button.addEventListener("click", async e => {
        try {
          // Ask the user to authenticate with the second factor; this will trigger a browser prompt
          const response = await authenticateTwoFactor();
          const { authStatus } = response;
          if (authStatus === authStatuses.COMPLETE) {
            // The user is properly authenticated => Navigate to the Account page
            location.href = "/account";
          } else {
            throw new Error("Two-factor authentication failed");
          }
        } catch (e) {
          // Alert the user that something went wrong
          alert(`Two-factor authentication failed. ${e}`);
        }
      });
    </script>
  </body>
</html>
4, tambahkan kode yang secara bersyarat menavigasi pengguna ke halaman autentikasi faktor kedua jika mereka telah menyiapkan 2FA.

Dalam codelab ini, membuat kredensial secara otomatis mengikutsertakan pengguna ke autentikasi 2 langkah.

Perhatikan bahwa

async function authenticateTwoFactor() {
  // Fetch the 2F options from the backend
  const optionsFromServer = await _fetch("/auth/two-factor-options", "POST");
  // Decode them
  const decodedOptions = decodeServerOptions(optionsFromServer);
  // Get a credential via the browser API; this will prompt the user to touch their security key or tap a button on their phone
  const credential = await navigator.credentials.get({
    publicKey: decodedOptions
  });
  // Encode the credential
  const encodedCredential = encodeCredential(credential);
  // Send it to the backend for verification
  return await _fetch("/auth/authenticate-two-factor", "POST", {
    credential: encodedCredential
  });
}
3 juga mengimplementasikan pemeriksaan sesi sisi server, yang memastikan bahwa hanya pengguna terautentikasi yang dapat mengakses
<script type="module">
    // ... (imports)
    // Initialize the credential list by updating it once on page load
    updateCredentialList();
3.

navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
0

Cobalah! 👩🏻‍💻

  • Login dengan pengguna baru johndoe.
  • Logout.
  • Login ke akun Anda sebagai johndoe; perhatikan bahwa hanya sandi yang diperlukan.
  • Buat kredensial. Hal ini berarti Anda telah mengaktifkan autentikasi 2 langkah sebagai johndoe.
  • Logout.
  • Masukkan nama pengguna johndoe dan sandi Anda.
  • Lihat bagaimana Anda secara otomatis dinavigasikan ke halaman autentikasi faktor kedua.
  • (Coba akses halaman Akun di
        <main>
    ...
        </main>
        <script type="module">
          import { authenticateTwoFactor, authStatuses } from "/auth.client.js";
    
          const button = document.querySelector("#authenticateButton");
          button.addEventListener("click", async e => {
            try {
              // Ask the user to authenticate with the second factor; this will trigger a browser prompt
              const response = await authenticateTwoFactor();
              const { authStatus } = response;
              if (authStatus === authStatuses.COMPLETE) {
                // The user is properly authenticated => Navigate to the Account page
                location.href = "/account";
              } else {
                throw new Error("Two-factor authentication failed");
              }
            } catch (e) {
              // Alert the user that something went wrong
              alert(`Two-factor authentication failed. ${e}`);
            }
          });
        </script>
      </body>
    </html>
    
    7; perhatikan bagaimana Anda dialihkan ke halaman indeks karena Anda belum sepenuhnya diautentikasi: Anda tidak memiliki faktor kedua)
  • Kembali ke halaman autentikasi faktor kedua, lalu klik Gunakan kunci keamanan untuk mengautentikasi faktor kedua.
  • Sekarang Anda telah login dan akan melihat halaman Akun Anda.

8. Memudahkan penggunaan kredensial

Kapan saja dalam codelab ini, Anda dapat melihat kode akhir (dan aplikasi web) sebagai referensi.

Anda telah mempelajari fungsi dasar autentikasi 2 langkah dengan kunci keamanan 🚀

Tapi... Apakah Anda memperhatikan?

Untuk saat ini, daftar kredensial kita tidak praktis: ID kredensial dan kunci publik adalah string panjang yang tidak membantu saat mengelola kredensial. Manusia tidak terlalu baik dalam hal string dan angka yang panjang 🤖

Jadi, mari kita tingkatkan, dan tambahkan fungsi untuk memberi nama dan mengganti nama kredensial dengan string yang dapat dibaca manusia.

Sebaiknya Anda tahu

Nama kredensial bukan bagian dari spesifikasi. Secara default, kredensial hanya memiliki ID. Menambahkan nama adalah hal yang kita lakukan di sini hanya untuk memudahkan pengguna.

Selain itu, beberapa tugas sudah diselesaikan: kita telah menyesuaikan library sisi server dan menambahkan kolom

    <main>
...
    </main>
    <script type="module">
      import { authenticateTwoFactor, authStatuses } from "/auth.client.js";

      const button = document.querySelector("#authenticateButton");
      button.addEventListener("click", async e => {
        try {
          // Ask the user to authenticate with the second factor; this will trigger a browser prompt
          const response = await authenticateTwoFactor();
          const { authStatus } = response;
          if (authStatus === authStatuses.COMPLETE) {
            // The user is properly authenticated => Navigate to the Account page
            location.href = "/account";
          } else {
            throw new Error("Two-factor authentication failed");
          }
        } catch (e) {
          // Alert the user that something went wrong
          alert(`Two-factor authentication failed. ${e}`);
        }
      });
    </script>
  </body>
</html>
8 untuk kredensial yang Anda simpan di database. Periksa
    <main>
...
    </main>
    <script type="module">
      import { authenticateTwoFactor, authStatuses } from "/auth.client.js";

      const button = document.querySelector("#authenticateButton");
      button.addEventListener("click", async e => {
        try {
          // Ask the user to authenticate with the second factor; this will trigger a browser prompt
          const response = await authenticateTwoFactor();
          const { authStatus } = response;
          if (authStatus === authStatuses.COMPLETE) {
            // The user is properly authenticated => Navigate to the Account page
            location.href = "/account";
          } else {
            throw new Error("Two-factor authentication failed");
          }
        } catch (e) {
          // Alert the user that something went wrong
          alert(`Two-factor authentication failed. ${e}`);
        }
      });
    </script>
  </body>
</html>
9 untuk melihat kode.

Melihat renameCredential

Fungsi untuk mengganti nama kredensial telah ditambahkan untuk Anda dalam kode awal, di

navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
00, guna menghemat waktu saat menerapkan fungsi ini yang tidak terlalu berpengaruh.

navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
1

Ini adalah panggilan update database rutin: klien mengirimkan permintaan

navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
01 ke backend, dengan ID kredensial dan nama baru untuk kredensial tersebut.

Mengimplementasikan nama kredensial kustom

Di

<script type="module">
    // ... (imports)
    // Initialize the credential list by updating it once on page load
    updateCredentialList();
3, perhatikan fungsi kosong
navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
03.

Tambahkan kode berikut:

navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
2

Mungkin lebih masuk akal untuk memberi nama kredensial hanya setelah kredensial berhasil dibuat. Jadi, mari kita buat kredensial tanpa nama, dan setelah berhasil dibuat, ganti nama kredensial tersebut. Namun, hal ini akan menghasilkan dua panggilan backend.

Gunakan fungsi

navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
03 di
navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
05, agar pengguna dapat menamai kredensial saat pendaftaran:

navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
3

Perhatikan bahwa input pengguna akan divalidasi dan dibersihkan dalam backend:

navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
4

Menampilkan nama kredensial

Buka

navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
06 di
navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
07.

Perlu diperhatikan bahwa sudah ada kode untuk menampilkan nama kredensial di bagian atas kartu kredensial:

navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
5

Cobalah! 👩🏻‍💻

  • Buat kredensial.
  • Anda akan diminta untuk menamainya.
  • Masukkan nama baru, lalu klik OK.
  • Kredensial kini telah diganti namanya.
  • Ulangi dan periksa apakah semuanya berjalan lancar saat kolom nama dibiarkan kosong.

Mengaktifkan penggantian nama kredensial

Pengguna mungkin perlu mengganti nama kredensial–misalnya, mereka menambahkan kunci kedua dan ingin mengganti nama kunci pertama mereka untuk dapat membedakannya dengan lebih baik.

Di

<script type="module">
    // ... (imports)
    // Initialize the credential list by updating it once on page load
    updateCredentialList();
3, cari fungsi
async function register() {
  let user = {};
  try {
    // ...
  } catch (e) {
    // ...
  }
  // Refresh the credential list to display the new credential
  await updateCredentialList();
}
2 yang masih kosong dan tambahkan kode berikut ke dalamnya:

navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
6

Sekarang, di

navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
06
navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
07, dalam div
navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
12, tambahkan kode berikut. Kode ini menambahkan tombol Ganti nama ke template kartu kredensial; saat diklik, tombol tersebut akan memanggil fungsi
async function register() {
  let user = {};
  try {
    // ...
  } catch (e) {
    // ...
  }
  // Refresh the credential list to display the new credential
  await updateCredentialList();
}
2 yang baru saja kita buat:

navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
7

Cobalah! 👩🏻‍💻

  • Klik Ganti nama.
  • Masukkan nama baru saat diminta.
  • Klik OK.
  • Kredensial seharusnya berhasil diganti namanya, dan daftar akan diperbarui secara otomatis.
  • Memuat ulang halaman akan tetap menampilkan nama baru (ini menunjukkan bahwa nama baru tetap dipertahankan di sisi server).

Menampilkan tanggal pembuatan kredensial

Tanggal pembuatan tidak ada di kredensial yang dibuat melalui

navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
14.

Namun, karena informasi ini dapat berguna bagi pengguna untuk membedakan kredensial, kami telah menyesuaikan library sisi server dalam kode awal untuk Anda, dan menambahkan kolom

navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
15 yang sama dengan
navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
16 setelah menyimpan kredensial baru.

Pada

navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
07 di
navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
18
<script type="module">
    // ... (imports)
    // Initialize the credential list by updating it once on page load
    updateCredentialList();
4, tambahkan hal berikut untuk menampilkan informasi tanggal pembuatan kepada pengguna:

navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
8

9. Membuat kode Anda sesuai untuk masa mendatang

Sejauh ini, kita hanya meminta pengguna mendaftarkan pengautentikasi roaming sederhana yang kemudian digunakan sebagai faktor kedua selama login.

Satu pendekatan yang lebih canggih adalah mengandalkan jenis pengautentikasi yang lebih kuat: pengautentikasi roaming pemverifikasi pengguna (UVRA). UVRA dapat memberikan dua faktor autentikasi dan ketahanan terhadap phishing dalam alur login satu langkah.

Seperti apa tampilan UVRA?

UVRA (pengautentikasi roaming pemverifikasi pengguna) dapat berupa:

  • Kunci keamanan dengan kemampuan biometrik seperti ini, dengan
    navigator.credentials.create({
        publicKey: {
        // Options generated server-side
        ...credentialCreationOptions
    // ...
    }
    
    20 sebagai
    navigator.credentials.create({
        publicKey: {
        // Options generated server-side
        ...credentialCreationOptions
    // ...
    }
    
    21
  • Atau ponsel yang dapat digunakan sebagai kunci keamanan, dengan
    navigator.credentials.create({
        publicKey: {
        // Options generated server-side
        ...credentialCreationOptions
    // ...
    }
    
    20 sebagai
    navigator.credentials.create({
        publicKey: {
        // Options generated server-side
        ...credentialCreationOptions
    // ...
    }
    
    23.
    navigator.credentials.create({
        publicKey: {
        // Options generated server-side
        ...credentialCreationOptions
    // ...
    }
    
    23 adalah singkatan dari Bluetooth Hemat Energi yang dibantu cloud. Pelajari lebih lanjut di Mendaftarkan ponsel sebagai UVRA.

Idealnya, Anda mendukung kedua pendekatan tersebut. Untuk melakukannya, Anda harus menyesuaikan pengalaman pengguna:

  • Jika pengguna hanya memiliki pengautentikasi roaming sederhana (yang bukan pemverifikasi pengguna), izinkan mereka menggunakannya untuk mencapai bootstrap akun tahan phishing, namun mereka juga harus mengetik nama pengguna dan sandi. Inilah yang sudah dilakukan oleh codelab kita.
  • Jika pengguna lain memiliki pengautentikasi roaming pemverifikasi pengguna yang lebih canggih, mereka akan dapat melewati langkah sandi—dan mungkin juga langkah nama pengguna—selama bootstrap akun.

Pelajari hal ini lebih lanjut di Melakukan Bootstrap Akun Tahan Phishing dengan Login Tanpa Sandi Opsional.

Dalam codelab ini, kita tidak akan menyesuaikan pengalaman pengguna, tetapi kita akan menyiapkan codebase agar Anda memiliki data yang diperlukan untuk menyesuaikan pengalaman pengguna.

Anda memerlukan dua hal:

  • Menetapkan
    // Update the list that displays credentials
    async function updateCredentialList() {
      // Fetch the latest credential list from the backend
      const response = await _fetch('/auth/credentials', 'GET');
      const credentials = response.credentials || [];
      // Generate the credential list as HTML and pass remove/rename functions as args
      const credentialListHtml = getCredentialListHtml(
        credentials,
        removeEl,
        renameEl
      );
      // Display the list of credentials in the DOM
      const list = document.querySelector('#credentials');
      render(credentialListHtml, list);
    }
    
    4 di setelan backend Anda. Hal ini sudah dilakukan untuk Anda.
  • Menyiapkan cara untuk mengetahui apakah kredensial yang dapat ditemukan (juga disebut kunci penduduk) dibuat atau tidak.

Untuk mengetahui apakah kredensial yang dapat ditemukan dibuat atau tidak:

  • Buat kueri nilai
    navigator.credentials.create({
        publicKey: {
        // Options generated server-side
        ...credentialCreationOptions
    // ...
    }
    
    26 setelah pembuatan kredensial (
    navigator.credentials.create({
        publicKey: {
        // Options generated server-side
        ...credentialCreationOptions
    // ...
    }
    
    27).
  • Buat kueri nilai
    navigator.credentials.create({
        publicKey: {
        // Options generated server-side
        ...credentialCreationOptions
    // ...
    }
    
    28 setelah pembuatan kredensial. Tindakan ini akan membantu Anda menentukan apakah dasar platform mendukung fungsi UVRA, misalnya apakah benar-benar telepon seluler.
  • Simpan nilai
    navigator.credentials.create({
        publicKey: {
        // Options generated server-side
        ...credentialCreationOptions
    // ...
    }
    
    26 dan
    navigator.credentials.create({
        publicKey: {
        // Options generated server-side
        ...credentialCreationOptions
    // ...
    }
    
    28 di backend. Hal ini sudah dilakukan untuk Anda di kode awal. Lihat
     <div class="flex-h-between">
        <h3>
            Two-factor authentication
        </h3>
        <button class="create" id="registerButton" raised>
            ➕ Add a credential
        </button>
    </div>
    
    6 jika Anda ingin tahu.

Tentang

navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
26

navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
26 disebut ekstensi: ini adalah cara untuk melengkapi mekanisme dalam membuat kredensial, agar sesuai dengan kasus penggunaan tertentu. Pelajari lebih lanjut di ekstensi WebAuthn.

Mari kita dapatkan nilai

navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
26 dan
navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
28, lalu kirimkan ke backend. Di
navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
00, ubah
<div class="flex-h-between">
(HTML you've just added)
</div>
<div id="credentials"></div>
1 sebagai berikut:

  • Tambahkan kolom
    navigator.credentials.create({
        publicKey: {
        // Options generated server-side
        ...credentialCreationOptions
    // ...
    }
    
    38 saat memanggil
    navigator.credentials.create({
        publicKey: {
        // Options generated server-side
        ...credentialCreationOptions
    // ...
    }
    
    39
  • Siapkan
    navigator.credentials.create({
        publicKey: {
        // Options generated server-side
        ...credentialCreationOptions
    // ...
    }
    
    40 dan
    navigator.credentials.create({
        publicKey: {
        // Options generated server-side
        ...credentialCreationOptions
    // ...
    }
    
    41 sebelum mengirimkan kredensial ke backend untuk disimpan.

<div class="flex-h-between">
(HTML you've just added)
</div>
<div id="credentials"></div>
1 akan terlihat seperti berikut:

navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
9

10. Memastikan dukungan lintas browser

Mendukung browser non-Chromium

Dalam fungsi

<div class="flex-h-between">
(HTML you've just added)
</div>
<div id="credentials"></div>
1
 <div class="flex-h-between">
    <h3>
        Two-factor authentication
    </h3>
    <button class="create" id="registerButton" raised>
        ➕ Add a credential
    </button>
</div>
8, kita memanggil
navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
45 pada kredensial yang baru dibuat untuk menyimpan informasi ini di backend sebagai petunjuk ke server.

Namun,

navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
46 saat ini tidak diimplementasikan di semua browser (tidak seperti
navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
47 yang didukung di seluruh browser): panggilan
navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
46 akan menampilkan error di Firefox dan Safari, yang akan mencegah pembuatan kredensial di browser ini.

Untuk memastikan kode Anda berjalan di semua browser utama, gabungkan panggilan

navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
40 dalam kondisi:

 <div class="flex-h-between">
    <h3>
        Two-factor authentication
    </h3>
    <button class="create" id="registerButton" raised>
        ➕ Add a credential
    </button>
</div>
0

Perlu diketahui bahwa pada server,

navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
28 ditetapkan ke
navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
51. Di Firefox dan Safari, daftar
navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
28 tidak akan menjadi
navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
53, tetapi daftar kosong
navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}
54, yang mencegah error.

Berikan peringatan kepada pengguna yang menggunakan browser yang tidak mendukung WebAuthn

Untuk mendapatkan kunci keamanan Anda buka g co sc di jendela browser baru

Meskipun WebAuthn didukung di semua browser utama, sebaiknya tampilkan peringatan di browser yang tidak mendukung WebAuthn.

Di

    <main>
...
    </main>
    <script type="module">
      import { authenticateTwoFactor, authStatuses } from "/auth.client.js";

      const button = document.querySelector("#authenticateButton");
      button.addEventListener("click", async e => {
        try {
          // Ask the user to authenticate with the second factor; this will trigger a browser prompt
          const response = await authenticateTwoFactor();
          const { authStatus } = response;
          if (authStatus === authStatuses.COMPLETE) {
            // The user is properly authenticated => Navigate to the Account page
            location.href = "/account";
          } else {
            throw new Error("Two-factor authentication failed");
          }
        } catch (e) {
          // Alert the user that something went wrong
          alert(`Two-factor authentication failed. ${e}`);
        }
      });
    </script>
  </body>
</html>
2, amati kehadiran div ini:

 <div class="flex-h-between">
    <h3>
        Two-factor authentication
    </h3>
    <button class="create" id="registerButton" raised>
        ➕ Add a credential
    </button>
</div>
1

Di skrip inline

    <main>
...
    </main>
    <script type="module">
      import { authenticateTwoFactor, authStatuses } from "/auth.client.js";

      const button = document.querySelector("#authenticateButton");
      button.addEventListener("click", async e => {
        try {
          // Ask the user to authenticate with the second factor; this will trigger a browser prompt
          const response = await authenticateTwoFactor();
          const { authStatus } = response;
          if (authStatus === authStatuses.COMPLETE) {
            // The user is properly authenticated => Navigate to the Account page
            location.href = "/account";
          } else {
            throw new Error("Two-factor authentication failed");
          }
        } catch (e) {
          // Alert the user that something went wrong
          alert(`Two-factor authentication failed. ${e}`);
        }
      });
    </script>
  </body>
</html>
2, tambahkan kode berikut untuk menampilkan banner di browser yang tidak mendukung WebAuthn:

 <div class="flex-h-between">
    <h3>
        Two-factor authentication
    </h3>
    <button class="create" id="registerButton" raised>
        ➕ Add a credential
    </button>
</div>
2

Dalam aplikasi web yang sebenarnya, Anda akan melakukan hal yang lebih rumit dan memiliki mekanisme penggantian yang sesuai untuk browser tersebut—tetapi hal ini menunjukkan cara memeriksa dukungan WebAuthn.

11. Kerja bagus!

✨Selesai!

Anda telah mengimplementasikan autentikasi 2 langkah dengan kunci keamanan.

Dalam codelab ini, kita telah membahas dasar-dasarnya. Jika Anda ingin menjelajahi WebAuthn untuk 2FA lebih lanjut, berikut beberapa ide yang dapat Anda coba selanjutnya:

Bagaimana cara membuka kunci keamanan facebook?

Pelajari selengkapnya tentang kunci keamanan dan cara kerjanya..
Buka Pengaturan keamanan dan login..
Ketuk Gunakan autentikasi dua faktor. Anda mungkin diminta memasukkan lagi kata sandi Facebook Anda pada tahap ini..
Ketuk Gunakan kunci keamanan. Lalu, ikuti petunjuk di layar..

Dimana letak kunci keamanan?

Di ponsel Android, buka myaccount.google.com/security. Pada "Login ke Google", pilih Verifikasi 2 Langkah. Anda mungkin perlu login. Di kiri bawah, ketuk Tambahkan kunci keamanan.

Gimana cara mendapatkan kode keamanan Google?

Memverifikasi Akun Google.
Di ponsel, cari Setelan Google Anda. Bergantung pada perangkat Anda: ... .
Ketuk Kelola Akun Google Anda..
Scroll ke kanan dan ketuk Keamanan. Kode keamanan. ... .
Kode 10 digit akan tersedia..
Masukkan kode di ponsel yang ingin digunakan untuk login, lalu ketuk Lanjutkan..

Apa itu tombol di kunci keamanan?

Kunci keamanan merupakan sebuah metode autentikasi dua faktor yang menambahkan lapisan keamanan untuk login, biasanya digunakan dengan dua metode autentikasi dua faktor lainnya seperti kode pesan teks (SMS) atau aplikasi autentikasi pihak ketiga.