Login dengan kunci sandi melalui isi otomatis formulir

Buat pengalaman login yang memanfaatkan kunci sandi sekaligus tetap mengakomodasi pengguna yang masih menggunakan sandi.

Panduan ini menjelaskan cara menggunakan isi otomatis formulir untuk memungkinkan pengguna login dengan kunci sandi bersama dengan sandi. Penggunaan pengisian formulir otomatis menciptakan pengalaman login yang terpadu, sehingga menyederhanakan transisi dari sandi ke metode autentikasi kunci sandi yang lebih aman dan mudah digunakan.

Pelajari cara menerapkan UI bersyarat WebAuthn untuk mendukung pengguna kunci sandi dan sandi dengan hambatan minimal di formulir login yang ada.

Mengapa menggunakan isi otomatis formulir untuk login dengan kunci sandi?

Kunci sandi memungkinkan pengguna login ke situs menggunakan sidik jari, wajah, PIN perangkat mereka.

Jika semua pengguna memiliki kunci sandi, alur autentikasi dapat berupa tombol login tunggal. Dengan mengetuk tombol, pengguna dapat langsung memverifikasi akun dengan kunci layar, dan login.

Namun, peralihan dari sandi ke kunci sandi menimbulkan tantangan. Situs harus mendukung pengguna sandi dan kunci sandi selama periode ini. Mengharapkan pengguna mengingat situs mana yang menggunakan kunci sandi dan meminta mereka memilih metode login di awal akan menciptakan pengalaman pengguna yang buruk.

Kunci sandi juga merupakan teknologi baru, dan menjelaskannya dengan jelas bisa jadi sulit. Penggunaan antarmuka isi otomatis yang sudah dikenal membantu mengatasi tantangan transisi dan kebutuhan pengguna untuk merasa nyaman.

Menggunakan UI bersyarat

Untuk mendukung pengguna kunci sandi dan sandi secara efektif, sertakan kunci sandi dalam saran isi otomatis formulir Anda. Pendekatan ini menggunakan UI kondisional, fitur dari standar WebAuthn.

Contoh pemilihan kunci sandi melalui isi otomatis formulir.

Saat pengguna fokus pada kolom input nama pengguna, dialog isi otomatis akan muncul, menyarankan kunci sandi tersimpan bersama dengan sandi tersimpan. Pengguna dapat memilih kunci sandi atau sandi dan melanjutkan untuk login, menggunakan kunci layar perangkat jika mereka memilih kunci sandi.

Dengan demikian, pengguna dapat login ke situs Anda dengan formulir login yang ada, tetapi dengan manfaat keamanan tambahan dari kunci sandi jika mereka memilikinya.

Cara kerja autentikasi kunci sandi

Untuk mengautentikasi dengan kunci sandi, Anda menggunakan WebAuthn API.

Empat komponen dalam alur autentikasi kunci sandi adalah:

  • Backend: Menyimpan detail akun pengguna, termasuk kunci publik.
  • Frontend: Berkomunikasi dengan browser dan mengambil data yang diperlukan dari backend.
  • Browser: Menjalankan JavaScript Anda dan berinteraksi dengan WebAuthn API.
  • Penyedia kunci sandi: Membuat dan menyimpan kunci sandi. Biasanya berupa pengelola sandi seperti Pengelola Sandi Google, atau kunci keamanan.
Alur autentikasi kunci sandi, yang menunjukkan interaksi antara frontend, backend, browser, dan penyedia kunci sandi.
Alur autentikasi kunci sandi lengkap.

Proses autentikasi kunci sandi mengikuti alur berikut:

  1. Pengguna membuka halaman login, dan frontend meminta tantangan autentikasi dari backend.
  2. Backend membuat dan menampilkan tantangan WebAuthn yang terkait dengan akun pengguna.
  3. Frontend memanggil navigator.credentials.get() dengan tantangan untuk memulai autentikasi menggunakan browser.
  4. Browser, yang berinteraksi dengan penyedia kunci sandi, akan meminta pengguna untuk memilih kunci sandi (sering kali menggunakan dialog isi otomatis yang dipicu dengan memfokuskan kolom login) dan memverifikasi identitas mereka menggunakan kunci layar perangkat atau biometrik.
  5. Setelah verifikasi pengguna berhasil, penyedia kunci sandi menandatangani tantangan, dan browser menampilkan kredensial kunci publik yang dihasilkan (termasuk tanda tangan) ke frontend.
  6. Frontend mengirimkan kredensial ini ke backend.
  7. Backend memverifikasi tanda tangan kredensial terhadap kunci publik tersimpan pengguna. Jika verifikasi berhasil, backend akan membuat pengguna login.

Mengautentikasi dengan kunci sandi melalui isi otomatis formulir

Untuk memulai autentikasi kunci sandi menggunakan isi otomatis formulir, lakukan panggilan WebAuthn get bersyarat saat halaman login dimuat. Panggilan ke navigator.credentials.get() ini mencakup opsi mediation: 'conditional'.
Permintaan bersyarat ke API navigator.credentials.get() WebAuthn tidak menampilkan UI secara langsung. Sebagai gantinya, aplikasi akan menunggu dalam status tertunda hingga pengguna berinteraksi dengan perintah pengisian otomatis kolom nama pengguna. Jika pengguna memilih kunci sandi, browser akan menyelesaikan promise yang tertunda dengan kredensial untuk membuat pengguna login, melewati pengiriman formulir tradisional. Jika pengguna memilih sandi, promise tidak akan diselesaikan, dan alur login sandi standar akan berlanjut.rm. Kemudian, halaman akan bertanggung jawab untuk login pengguna.

Kolom input formulir anotasi

Untuk mengaktifkan pengisian otomatis kunci sandi, tambahkan atribut autocomplete ke kolom nama pengguna input formulir Anda. Sertakan username dan webauthn sebagai nilai yang dipisahkan dengan spasi.

<input type="text" name="username" autocomplete="username webauthn" autofocus>

Menambahkan autofocus ke kolom ini akan otomatis memicu perintah isi otomatis saat halaman dimuat, sehingga menampilkan sandi dan kunci sandi yang tersedia secara langsung.

Deteksi fitur

Sebelum memanggil panggilan API WebAuthn bersyarat, periksa apakah:

  • Browser mendukung WebAuthn dengan PublicKeyCredential.

Browser Support

  • Chrome: 67.
  • Edge: 18.
  • Firefox: 60.
  • Safari: 13.

Source

  • Browser mendukung deteksi kemampuan dengan PublicKeyCredential.getClientCapabilities().

Browser Support

  • Chrome: 133.
  • Edge: 133.
  • Firefox: 135.
  • Safari: 17.4.

Source

Cuplikan berikut menunjukkan cara memeriksa apakah browser mendukung fitur ini:

if (window.PublicKeyCredential && PublicKeyCredential.getClientCapabilities) {
  const capabilities = await PublicKeyCredential.getClientCapabilities();
  // Check if conditional mediation is available.  
  if (capabilities.conditionalGet === true) {
    // The browser supports conditional mediation.
  }
}

Mengambil informasi dari backend

Backend Anda harus menyediakan beberapa opsi ke frontend untuk memulai panggilan navigator.credentials.get(). Opsi ini biasanya diambil sebagai objek JSON dari endpoint di server Anda.

Properti utama dalam objek opsi meliputi:

  • challenge: Challenge yang dibuat server dalam ArrayBuffer (biasanya dienkode Base64URL untuk transportasi JSON). Hal ini penting untuk mencegah serangan replay. Server Anda harus membuat tantangan baru untuk setiap upaya login dan harus membatalkannya setelah beberapa saat atau jika upaya login gagal.
  • allowCredentials: Array deskriptor kredensial. Teruskan array kosong. Tindakan ini akan meminta browser untuk mencantumkan semua kredensial untuk rpId yang ditentukan.
  • userVerification: Menentukan preferensi Anda untuk verifikasi pengguna, seperti mewajibkan kunci layar perangkat. Nilai default dan yang direkomendasikan adalah "preferred". Kemungkinan nilainya adalah:

    • "required": Verifikasi pengguna harus dilakukan oleh pengautentikasi (seperti PIN atau biometrik). Operasi akan gagal jika verifikasi tidak dapat dilakukan.
    • "preferred": Pengautentikasi mencoba verifikasi pengguna, tetapi operasi dapat berhasil tanpa verifikasi.
    • "discouraged": Pengautentikasi harus menghindari verifikasi pengguna jika memungkinkan.
  • rpId: ID pihak tepercaya Anda, biasanya domain situs Anda (seperti example.com). Nilai ini harus sama persis dengan rp.id yang digunakan saat kredensial kunci sandi dibuat.

Server Anda harus membuat objek opsi ini. Nilai ArrayBuffer (seperti challenge) harus dienkode Base64URL untuk transportasi JSON. Di frontend, setelah mengurai JSON, gunakan PublicKeyCredential.parseRequestOptionsFromJSON() untuk mengonversi objek (termasuk mendekode string Base64URL) ke dalam format yang diharapkan navigator.credentials.get().

Cuplikan kode berikut menunjukkan cara Anda dapat mengambil dan mendekode informasi yang diperlukan untuk mengautentikasi dengan kunci sandi.

// Fetch an encoded PubicKeyCredentialRequestOptions from the server.
const _options = await fetch('/webauthn/signinRequest');

// Deserialize and decode the PublicKeyCredentialRequestOptions.
const decoded_options = JSON.parse(_options);
const options = PublicKeyCredential.parseRequestOptionsFromJSON(decoded_options);
...

Panggil WebAuthn API dengan tanda conditional untuk mengautentikasi pengguna

Setelah objek publicKeyCredentialRequestOptions (disebut sebagai options dalam contoh kode di bawah) disiapkan, panggil navigator.credentials.get() untuk memulai autentikasi kunci sandi bersyarat.

// To abort a WebAuthn call, instantiate an AbortController.
const abortController = new AbortController();

// Invoke WebAuthn to authenticate with a passkey.
const credential = await navigator.credentials.get({
  publicKey: options,
  signal: abortController.signal,
  // Specify 'conditional' to activate conditional UI
  mediation: 'conditional'
});

Parameter utama untuk panggilan ini:

  • publicKey: Ini harus berupa objek publicKeyCredentialRequestOptions (bernama options dalam contoh) yang Anda ambil dari server dan diproses pada langkah sebelumnya.
  • signal: Meneruskan sinyal AbortController (seperti abortController.signal) memungkinkan Anda membatalkan permintaan get() secara terprogram. Hal ini berguna saat Anda ingin memanggil WebAuthn lain.
  • mediation: 'conditional': Ini adalah flag penting yang membuat panggilan WebAuthn menjadi bersyarat. Tindakan ini memberi tahu browser untuk menunggu interaksi pengguna dengan perintah isi otomatis, bukan langsung menampilkan dialog modal.

Kirim kredensial kunci publik yang ditampilkan ke server RP

Jika pengguna memilih kunci sandi dan berhasil memverifikasi identitasnya (misalnya, menggunakan kunci layar perangkat), promise navigator.credentials.get() akan diselesaikan. Tindakan ini akan menampilkan objek PublicKeyCredential ke frontend Anda.

Janji dapat ditolak karena beberapa alasan. Anda harus menangani error ini dalam kode dengan memeriksa properti name dari objek Error:

  • NotAllowedError: Pengguna membatalkan operasi, atau tidak ada kunci sandi yang dipilih.
  • AbortError: Operasi dibatalkan, kemungkinan oleh kode Anda menggunakan AbortController.
  • Pengecualian lainnya: Terjadi error yang tidak terduga. Browser biasanya menampilkan dialog error kepada pengguna.

Objek PublicKeyCredential berisi beberapa properti. Properti utama yang relevan untuk autentikasi meliputi:

  • id: ID kredensial kunci sandi yang diautentikasi dan dienkode base64url.
  • rawId: Versi ArrayBuffer dari ID kredensial.
  • response.clientDataJSON: ArrayBuffer data klien. Kolom ini berisi informasi seperti verifikasi login dan asal yang harus diverifikasi oleh server Anda.
  • response.authenticatorData: ArrayBuffer data autentikator. Kolom ini menyertakan informasi seperti ID RP.
  • response.signature: ArrayBuffer yang berisi tanda tangan. Nilai ini adalah inti kredensial dan server Anda harus memverifikasi tanda tangan ini menggunakan kunci publik yang disimpan untuk kredensial .
  • response.userHandle: ArrayBuffer yang berisi ID pengguna yang diberikan selama pendaftaran kunci sandi.
  • authenticatorAttachment: Menunjukkan apakah pengautentikator adalah bagian dari perangkat klien (platform) atau eksternal (cross-platform). Lampiran cross-platform dapat terjadi jika pengguna login dengan ponsel. Dalam kasus seperti ini, pertimbangkan untuk meminta mereka membuat kunci sandi di perangkat saat ini untuk memudahkan penggunaan di masa mendatang.
  • type: Kolom ini selalu ditetapkan ke "public-key".

Untuk mengirim objek PublicKeyCredential ini ke backend Anda, panggil metode .toJSON() terlebih dahulu. Metode ini membuat versi kredensial yang dapat diserialisasi JSON, yang menangani konversi properti ArrayBuffer (seperti rawId, clientDataJSON, authenticatorData, signature, dan userHandle) dengan benar ke string yang dienkode Base64URL. Kemudian, gunakan JSON.stringify() untuk mengonversi objek ini menjadi string dan kirimkan di isi permintaan Anda ke server.

...
// Encode and serialize the PublicKeyCredential.
const _result = credential.toJSON();
const result = JSON.stringify(_result);

// Encode and send the credential to the server for verification.  
const response = await fetch('/webauthn/signinResponse', {
  method: 'post',
  credentials: 'same-origin',
  body: result
});

Memverifikasi tanda tangan

Saat menerima kredensial kunci publik, server backend Anda harus memverifikasi keasliannya. Hal ini meliputi:

  1. Mengurai data kredensial.
  2. Mencari kunci publik tersimpan yang terkait dengan id kredensial.
  3. Memverifikasi signature yang diterima terhadap kunci publik yang disimpan.
  4. Memvalidasi data lain, seperti tantangan dan asal.

Sebaiknya gunakan library FIDO/WebAuthn sisi server untuk menangani operasi kriptografi ini dengan aman. Anda dapat menemukan library open source di repo GitHub awesome-webauthn.

Jika tanda tangan dan semua pernyataan lainnya valid, server dapat membuat pengguna login. Untuk mengetahui langkah-langkah validasi sisi server yang mendetail, lihat Autentikasi kunci sandi sisi server

Memberi sinyal jika kredensial yang cocok tidak ditemukan di backend

Jika server backend Anda tidak dapat menemukan kredensial dengan ID yang cocok selama login, pengguna mungkin telah menghapus kunci sandi ini dari server Anda sebelumnya tetapi tidak dari penyedia kunci sandi mereka. Ketidakcocokan ini dapat menyebabkan pengalaman pengguna yang membingungkan jika penyedia kunci sandi terus menyarankan kunci sandi yang tidak lagi berfungsi dengan situs Anda. Untuk meningkatkan keamanan ini, Anda harus memberi sinyal kepada penyedia kunci sandi untuk menghapus kunci sandi yang tidak terkait.

Anda dapat menggunakan metode PublicKeyCredential.signalUnknownCredential(), bagian dari Webauthn Signal API, untuk memberi tahu penyedia kunci sandi bahwa kredensial yang ditentukan telah dihapus atau tidak ada. Panggil metode statis ini di sisi klien jika server Anda menunjukkan (misalnya, dengan kode status HTTP tertentu seperti 404) bahwa ID kredensial yang ditampilkan tidak diketahui. Berikan ID RP dan ID kredensial yang tidak diketahui ke metode ini. Penyedia kunci sandi, jika mendukung sinyal, harus menghapus kunci sandi.

// Detect authentication failure due to lack of the credential
if (response.status === 404) {
  // Feature detection
  if (PublicKeyCredential.signalUnknownCredential) {
    await PublicKeyCredential.signalUnknownCredential({
      rpId: "example.com",
      credentialId: "vI0qOggiE3OT01ZRWBYz5l4MEgU0c7PmAA" // base64url encoded credential ID
    });
  } else {
    // Encourage the user to delete the passkey from the password manager nevertheless.
    ...
  }
}

Setelah autentikasi

Bergantung pada cara pengguna login, kami menyarankan alur yang berbeda untuk diikuti.

Jika pengguna telah login tanpa kunci sandi

Jika pengguna telah login ke situs Anda tanpa kunci sandi, mereka mungkin tidak memiliki kunci sandi yang terdaftar untuk akun tersebut atau di perangkat saat ini. Ini adalah waktu yang tepat untuk mendorong pembuatan kunci sandi. Pertimbangkan pendekatan berikut:

  • Mengupgrade sandi ke kunci sandi: Gunakan pembuatan bersyarat, fitur WebAuthn yang memungkinkan browser membuat kunci sandi secara otomatis untuk pengguna setelah login sandi berhasil. Hal ini dapat meningkatkan penggunaan kunci sandi secara signifikan dengan menyederhanakan proses pembuatannya. Pelajari cara kerjanya dan cara menerapkannya di Membantu pengguna mengadopsi kunci sandi dengan lebih lancar
  • Minta pembuatan kunci sandi secara manual: Dorong pengguna untuk membuat kunci sandi. Hal ini dapat efektif setelah pengguna menyelesaikan proses login yang lebih rumit, seperti autentikasi multi-faktor (MFA). Namun, hindari perintah yang berlebihan, yang dapat mengganggu pengalaman pengguna."

Untuk melihat cara mendorong pengguna membuat kunci sandi dan mempelajari praktik baik lainnya, lihat contoh di Mengomunikasikan kunci sandi kepada pengguna.

Jika pengguna telah login dengan kunci sandi

Setelah pengguna berhasil login dengan kunci sandi, Anda memiliki beberapa peluang untuk lebih meningkatkan pengalaman mereka dan mempertahankan konsistensi akun.

Mendorong pembuatan kunci sandi baru setelah autentikasi lintas perangkat

Jika pengguna login dengan kunci sandi menggunakan mekanisme lintas perangkat (misalnya, memindai kode QR dengan ponselnya), kunci sandi yang digunakannya mungkin tidak disimpan secara lokal di perangkat yang digunakan untuk login. Hal ini dapat terjadi jika:

  • Pengguna memiliki kunci sandi, tetapi di penyedia kunci sandi yang tidak mendukung sistem operasi atau browser untuk login.
  • Pengguna telah kehilangan akses ke penyedia kunci sandi di perangkat yang digunakan untuk login, tetapi kunci sandi masih tersedia di perangkat lain.

Dalam situasi ini, pertimbangkan untuk meminta pengguna membuat kunci sandi baru di perangkat saat ini. Hal ini dapat mencegah mereka mengulangi proses login lintas perangkat pada masa mendatang. Untuk menentukan apakah pengguna telah login menggunakan kunci sandi lintas perangkat, periksa properti authenticatorAttachment dari kredensial. Jika nilainya "cross-platform", hal ini menunjukkan autentikasi lintas perangkat. Jika ya, jelaskan kemudahan membuat kunci sandi baru dan pandu mereka melalui proses pembuatannya.

Menyinkronkan detail kunci sandi dengan penyedia menggunakan sinyal

Untuk memastikan konsistensi dan pengalaman pengguna yang lebih baik, Pihak Tepercaya (RP) Anda dapat menggunakan WebAuthn Signals API untuk mengomunikasikan pembaruan tentang kredensial dan informasi pengguna kepada penyedia kunci sandi.

Misalnya, agar daftar kunci sandi pengguna dari penyedia kunci sandi tetap akurat, sinkronkan kredensial di backend. Anda dapat memberi sinyal bahwa kunci sandi tidak ada lagi sehingga penyedia kunci sandi dapat menghapus kunci sandi yang tidak diperlukan.

Demikian pula, Anda dapat memberi sinyal jika pengguna memperbarui nama pengguna atau nama tampilannya di layanan Anda, untuk membantu menjaga informasi pengguna yang ditampilkan oleh penyedia kunci sandi (misalnya, dalam dialog pemilihan akun) tetap terbaru.

Untuk mempelajari lebih lanjut praktik baik dalam menjaga konsistensi kunci sandi, lihat Memastikan kunci sandi konsisten dengan kredensial di server Anda dengan Signal API.

Jangan meminta faktor kedua

Kunci sandi menawarkan perlindungan bawaan yang canggih terhadap ancaman umum seperti phishing. Oleh karena itu, faktor autentikasi kedua tidak menambahkan nilai keamanan yang signifikan. Sebaliknya, hal ini membuat langkah yang tidak perlu bagi pengguna selama proses login.

Checklist

  • Mengizinkan pengguna login dengan kunci sandi melalui isi otomatis formulir.
  • Memberi sinyal saat kredensial yang cocok dengan kunci sandi tidak ditemukan di backend.
  • Minta pengguna untuk membuat kunci sandi secara manual jika pengguna belum membuatnya setelah login.
  • Membuat kunci sandi secara otomatis (pembuatan bersyarat) setelah pengguna login dengan sandi (dan faktor kedua).
  • Minta pembuatan kunci sandi lokal jika pengguna telah login dengan kunci sandi lintas perangkat.
  • Memberi sinyal daftar kunci sandi yang tersedia dan detail pengguna yang diperbarui (nama pengguna, nama tampilan) kepada penyedia setelah login atau saat terjadi perubahan.

Resource