Mempelajari kredensial yang mudah ditemukan

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

Kunci keamanan versi 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 di 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 akan menampilkan daftar kunci sandi yang tersedia kepada pengguna, yang diidentifikasi oleh properti user.name yang 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 telah dibahas sebelumnya, tidak pernah menampilkan kredensial yang tidak dapat ditemukan.

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 memilih membuat kredensial yang dapat ditemukan, tetapi menerima kredensial yang tidak dapat ditemukan.
  • 'discouraged': RP lebih memilih 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 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, 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;
  
  // ...
}

Menampilkan 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 tetap akan muncul dan akan menawarkan pengguna untuk menampilkan kunci sandi dari perangkat lain. Saat melakukan transisi pengguna ke kunci sandi, sebaiknya hindari 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, dan pengguna yang tidak memiliki keduanya masih dapat mengetik nama pengguna dan sandinya.

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

Untuk mendapatkan pengalaman pengguna ini, selain meneruskan array kosong ke properti allowCredentials atau menghilangkan 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 salah satunya, mereka akan melalui verifikasi buka kunci perangkat reguler, dan baru kemudian promise yang ditampilkan oleh .get() akan diselesaikan dengan hasil. 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 membuat pengalaman pengguna ini di 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 ingin menggunakan kunci sandi tanpa browser atau OS 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 mendapatkan 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 'public-key'.
  • transports: Petunjuk transpor yang didukung oleh perangkat yang menyimpan kredensial ini, yang digunakan oleh browser untuk mengoptimalkan UI yang meminta pengguna untuk menampilkan perangkat eksternal. Daftar ini, jika disediakan, harus berisi hasil pemanggilan 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:

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

Gunakan kredensial yang dapat ditemukan dengan bijak. Dengan demikian, Anda dapat mendesain pengalaman login kunci sandi yang canggih yang akan dirasakan lancar oleh pengguna dan lebih cenderung untuk berinteraksi.