Mempelajari kredensial yang mudah ditemukan

Meskipun kredensial FIDO seperti kunci sandi bertujuan untuk menggantikan sandi, sebagian besar dari kredensial tersebut juga dapat membuat pengguna tidak perlu mengetik nama pengguna. Hal ini memungkinkan pengguna melakukan autentikasi dengan memilih akun dari daftar kunci sandi yang mereka miliki untuk situs saat ini.

Versi kunci keamanan sebelumnya dirancang sebagai metode autentikasi 2 langkah, dan memerlukan ID kredensial potensial, sehingga memerlukan entri nama pengguna. Kredensial yang dapat ditemukan kunci keamanan tanpa mengetahui ID-nya disebut kredensial yang dapat ditemukan. Sebagian besar kredensial FIDO yang dibuat saat ini adalah kredensial yang dapat ditemukan; terutama kunci sandi yang disimpan di pengelola sandi atau kunci keamanan modern.

Untuk memastikan kredensial Anda dibuat sebagai kunci sandi (kredensial yang dapat ditemukan), tentukan residentKey dan requireResidentKey saat kredensial dibuat.

Pihak tepercaya (RP) dapat menggunakan kredensial yang dapat ditemukan dengan menghapus allowCredentials selama autentikasi kunci sandi. Dalam kasus ini, browser atau sistem menampilkan daftar kunci sandi yang tersedia kepada pengguna, yang diidentifikasi oleh Properti user.name ditetapkan pada waktu pembuatan. Jika pengguna memilih salah satunya, nilai user.id akan disertakan dalam tanda tangan yang dihasilkan. Server kemudian dapat menggunakan ID kredensial tersebut atau ID kredensial yang ditampilkan untuk mencari akun, bukan nama pengguna yang diketik.

UI pemilih akun, seperti yang dibahas sebelumnya, tidak pernah menampilkan file yang tidak dapat ditemukan memiliki kredensial yang lengkap.

requireResidentKey dan residentKey

Untuk membuat kunci sandi, tentukan authenticatorSelection.residentKey dan authenticatorSelection.requireResidentKey di navigator.credentials.create() dengan nilai yang ditunjukkan sebagai berikut.

async function register () {
  // ...

  const publicKeyCredentialCreationOptions = {
    // ...
    authenticatorSelection: {
      authenticatorAttachment: 'platform',
      residentKey: 'required',
      requireResidentKey: true,
    }
  };

  const credential = await navigator.credentials.create({
    publicKey: publicKeyCredentialCreationOptions
  });

  // This does not run until the user selects a passkey.
  const credential = {};
  credential.id = cred.id;
  credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
  credential.type = cred.type;

  // ...
}

residentKey:

  • 'required': Kredensial yang dapat ditemukan harus dibuat. Jika tidak dapat dibuat, NotSupportedError akan ditampilkan.
  • 'preferred': RP lebih suka membuat kredensial yang dapat ditemukan, tetapi menerima kredensial yang tidak dapat ditemukan.
  • 'discouraged': RP lebih suka membuat kredensial yang tidak dapat ditemukan, tetapi menerima kredensial yang dapat ditemukan.

requireResidentKey:

  • Properti ini dipertahankan untuk kompatibilitas mundur dari WebAuthn Level 1, spesifikasi versi lama. Tetapkan string ini ke true jika residentKey adalah 'required', jika tidak, tetapkan ke false.

allowCredentials

RP dapat menggunakan allowCredentials di navigator.credentials.get() untuk mengontrol pengalaman autentikasi kunci sandi. Biasanya ada tiga jenis pengalaman autentikasi kunci sandi:

Dengan kredensial yang dapat ditemukan, RP dapat menampilkan pemilih akun modal bagi pengguna untuk memilih akun yang akan digunakan untuk login, yang diikuti dengan verifikasi pengguna. Hal ini cocok untuk alur autentikasi kunci sandi yang dimulai dengan menekan tombol khusus untuk autentikasi kunci sandi.

Untuk mencapai pengalaman pengguna ini, hapus atau teruskan array kosong ke parameter allowCredentials di navigator.credentials.get().

async function authenticate() {
  // ...

  const publicKeyCredentialRequestOptions = {
    // Server generated challenge:
    challenge: ****,
    // The same RP ID as used during registration:
    rpId: 'example.com',
    // You can omit `allowCredentials` as well:
    allowCredentials: []
  };

  const credential = await navigator.credentials.get({
    publicKey: publicKeyCredentialRequestOptions,
    signal: abortController.signal
  });

  // This does not run until the user selects a passkey.
  const credential = {};
  credential.id = cred.id;
  credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
  credential.type = cred.type;
  
  // ...
}

Tampilkan isi otomatis formulir kunci sandi

Pemilih akun modal yang dijelaskan di atas berfungsi dengan baik jika sebagian besar pengguna menggunakan kunci sandi dan menyediakannya di perangkat lokal. Untuk pengguna yang tidak memiliki kunci sandi lokal, dialog modal akan tetap muncul dan akan menawarkan kepada pengguna untuk menampilkan kunci sandi dari perangkat lain. Saat mentransisikan pengguna ke kunci sandi, sebaiknya Anda menghindari UI tersebut untuk pengguna yang belum menyiapkannya.

Sebagai gantinya, pemilihan kunci sandi dapat digabungkan ke perintah isi otomatis untuk kolom dalam formulir login biasa, bersama dengan nama pengguna dan sandi yang disimpan. Dengan cara ini, pengguna dengan kunci sandi dapat "mengisi" formulir login dengan memilih kunci sandinya, pengguna dengan pasangan nama pengguna/sandi tersimpan dapat memilihnya, sedangkan pengguna yang tidak memiliki keduanya masih dapat mengetikkan nama pengguna dan sandinya.

Pengalaman pengguna ini ideal jika RP sedang dalam migrasi dengan penggunaan campuran sandi dan kunci sandi.

Untuk mencapai pengalaman pengguna ini, selain meneruskan array kosong ke properti allowCredentials atau menghapus parameter, tentukan mediation: 'conditional' di navigator.credentials.get() dan anotasikan kolom input username HTML dengan autocomplete="username webauthn" atau kolom input password dengan autocomplete="password webauthn".

Panggilan ke navigator.credentials.get() tidak akan menyebabkan UI ditampilkan, tetapi jika pengguna memfokuskan kolom input yang dianotasi, kunci sandi yang tersedia akan disertakan dalam opsi isi otomatis. Jika pengguna memilih satu, mereka akan melalui verifikasi buka kunci perangkat reguler, dan hanya dengan cara ini promise yang ditampilkan oleh .get() akan di-resolve. Jika pengguna tidak memilih kunci sandi, promise tidak akan pernah diselesaikan.

async function authenticate() {
  // ...

  const publicKeyCredentialRequestOptions = {
    // Server generated challenge:
    challenge: ****,
    // The same RP ID as used during registration:
    rpId: 'example.com',
    // You can omit `allowCredentials` as well:
    allowCredentials: []
  };

  const cred = await navigator.credentials.get({
    publicKey: publicKeyCredentialRequestOptions,
    signal: abortController.signal,
    // Specify 'conditional' to activate conditional UI
    mediation: 'conditional'
  });

  // This does not run until the user selects a passkey.
  const credential = {};
  credential.id = cred.id;
  credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
  credential.type = cred.type;
  
  // ...
}
<input type="text" name="username" autocomplete="username webauthn" ...>

Anda dapat mempelajari cara membangun pengalaman pengguna ini di bagian Login dengan kunci sandi melalui isi otomatis formulir, serta codelab Mengimplementasikan kunci sandi dengan isi otomatis formulir di aplikasi web.

Autentikasi ulang

Dalam beberapa kasus, seperti saat menggunakan kunci sandi untuk autentikasi ulang, ID pengguna sudah diketahui. Dalam hal ini, kita akan menggunakan kunci sandi tanpa browser atau OS yang menampilkan bentuk pemilih akun apa pun. Hal ini dapat dilakukan dengan meneruskan daftar ID kredensial dalam parameter allowCredentials.

Dalam hal ini, jika salah satu kredensial yang disebutkan tersedia secara lokal, pengguna akan langsung diminta untuk membuka kunci perangkat. Jika tidak, pengguna akan diminta untuk menunjukkan perangkat lain (ponsel atau kunci keamanan) yang memiliki kredensial yang valid.

Untuk mencapai pengalaman pengguna ini, berikan daftar ID kredensial untuk pengguna yang login. RP harus dapat membuat kueri karena pengguna sudah diketahui. Berikan ID kredensial sebagai objek PublicKeyCredentialDescriptor di properti allowCredentials di navigator.credentials.get().

async function authenticate() {
  // ...

  const publicKeyCredentialRequestOptions = {
    // Server generated challenge:
    challenge: ****,
    // The same RP ID as used during registration:
    rpId: 'example.com',
    // Provide a list of PublicKeyCredentialDescriptors:
    allowCredentials: [{
      id: ****,
      type: 'public-key',
      transports: [
        'internal',
        'hybrid'
      ]
    }, {
      id: ****,
      type: 'public-key',
      transports: [
        'internal',
        'hybrid'
      ]
    }, ...]
  };

  const credential = await navigator.credentials.get({
    publicKey: publicKeyCredentialRequestOptions,
    signal: abortController.signal
  });

  // This does not run until the user selects a passkey.
  const credential = {};
  credential.id = cred.id;
  credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
  credential.type = cred.type;
  
  // ...
}

Objek PublicKeyCredentialDescriptor terdiri dari:

  • id: ID kredensial kunci publik yang telah diperoleh RP pada pendaftaran kunci sandi.
  • type: Kolom ini biasanya berupa 'public-key'.
  • transports: Petunjuk transpor yang didukung oleh perangkat yang menyimpan kredensial ini, yang digunakan oleh browser untuk mengoptimalkan UI yang meminta pengguna untuk mempresentasikan perangkat eksternal. Daftar ini, jika disediakan, harus berisi hasil panggilan getTransports() selama pendaftaran setiap kredensial.

Ringkasan

Kredensial yang dapat ditemukan membuat pengalaman login kunci sandi jauh lebih mudah digunakan dengan memungkinkan pengguna melewati entri nama pengguna. Dengan kombinasi residentKey, requireResidentKey, dan allowCredentials, RP dapat mencapai pengalaman login yang:

  • Tampilkan pemilih akun modal.
  • Menampilkan isi otomatis formulir kunci sandi.
  • Autentikasi ulang.

Gunakan kredensial yang dapat ditemukan dengan bijak. Dengan demikian, Anda dapat merancang pengalaman login dengan kunci sandi yang canggih yang akan memudahkan pengguna untuk berinteraksi.