1. Sebelum memulai
Penggunaan kunci sandi sebagai pengganti sandi merupakan cara tepat bagi situs untuk membuat akun penggunanya jadi lebih mudah, lebih simpel, dan lebih aman digunakan. Dengan kunci sandi, pengguna dapat login ke situs atau aplikasi menggunakan fitur kunci layar perangkat, seperti sidik jari, wajah, atau PIN perangkat. Kunci sandi harus dibuat, dikaitkan dengan akun pengguna, dan disimpan kunci publiknya di server sebelum pengguna dapat menggunakannya untuk login.
Dalam codelab ini, Anda akan mengubah proses login yang menggunakan nama pengguna dan sandi berbasis formulir menjadi proses login yang mendukung kunci sandi dan mencakup hal berikut:
- Tombol yang membuat kunci sandi setelah pengguna login.
- UI yang menampilkan daftar kunci sandi terdaftar.
- Formulir login yang sudah ada yang memungkinkan pengguna login dengan kunci sandi terdaftar melalui isi otomatis formulir.
Prasyarat
- Pemahaman dasar tentang JavaScript
- Pemahaman dasar tentang kunci sandi
- Pemahaman dasar tentang Web Authentication API (WebAuthn)
Yang akan Anda pelajari
- Cara membuat kunci sandi.
- Cara mengautentikasi pengguna dengan kunci sandi.
- Cara membuat formulir menyarankan kunci sandi sebagai opsi login.
Yang Anda butuhkan
Salah satu dari kombinasi perangkat berikut:
- Google Chrome dengan perangkat Android yang menjalankan Android 9 atau yang lebih tinggi, sebaiknya yang memiliki sensor biometrik.
- Chrome dengan perangkat Windows yang menjalankan Windows 10 atau yang lebih tinggi.
- Safari 16 atau yang lebih tinggi dengan iPhone yang menjalankan iOS 16 atau yang lebih tinggi, atau iPad yang menjalankan iPadOS 16 atau yang lebih tinggi.
- Safari 16 atau yang lebih tinggi, atau Chrome dengan perangkat desktop Apple yang menjalankan macOS Ventura atau yang lebih tinggi.
2. Memulai persiapan
Dalam codelab ini, Anda akan menggunakan layanan bernama Glitch, yang memungkinkan Anda mengedit kode sisi server dan klien dengan JavaScript, lalu men-deploy-nya hanya dari browser.
Membuka project
- Buka project di Glitch.
- Klik Remix untuk melakukan fork project Glitch.
- Pada menu navigasi di bagian bawah Glitch, klik Preview > Preview di jendela baru. Tab baru akan terbuka di browser Anda.
Memeriksa status awal situs
- Di tab pratinjau, masukkan nama pengguna acak, lalu klik Next.
- Masukkan sandi acak, lalu klik Sign-in. Sandi akan diabaikan, tetapi Anda masih diautentikasi dan akan diarahkan ke halaman beranda.
- Anda dapat mengubah nama tampilan jika perlu. Anda hanya dapat melakukan hal ini dalam status awal.
- Klik Sign out.
Dalam status ini, pengguna harus memasukkan sandi setiap kali mereka login. Tambahkan dukungan kunci sandi ke formulir ini agar pengguna dapat login dengan fungsi kunci layar perangkat. Anda dapat mencoba status akhir di https://passkeys-codelab.glitch.me/.
Untuk mengetahui informasi selengkapnya tentang cara kerja kunci sandi, lihat Bagaimana cara kerja kunci sandi?.
3. Menambahkan kemampuan untuk membuat kunci sandi
Agar pengguna dapat melakukan autentikasi dengan kunci sandi, Anda perlu memberi mereka kemampuan untuk membuat dan mendaftarkan kunci sandi, serta menyimpan kunci publiknya di server.
Izinkan pembuatan kunci sandi setelah pengguna login dengan sandi, dan tambahkan UI yang memungkinkan pengguna membuat kunci sandi dan melihat daftar semua kunci sandi yang terdaftar di halaman /home
. Di bagian berikutnya, Anda akan membuat fungsi yang membuat dan mendaftarkan kunci sandi.
Membuat fungsi registerCredential()
- Di Glitch, buka file
public/client.js
, lalu scroll ke bagian paling bawah. - Setelah bagian komentar yang relevan, tambahkan fungsi
registerCredential()
berikut:
public/client. js
// TODO: Add an ability to create a passkey: Create the registerCredential() function.
export async function registerCredential() {
// TODO: Add an ability to create a passkey: Obtain the challenge and other options from the server endpoint.
// TODO: Add an ability to create a passkey: Create a credential.
// TODO: Add an ability to create a passkey: Register the credential to the server endpoint.
};
Fungsi ini membuat dan mendaftarkan kunci sandi di server.
Mendapatkan verifikasi login dan opsi lainnya dari endpoint server
Sebelum kunci sandi dibuat, Anda harus meminta parameter untuk meneruskan WebAuthn dari server, termasuk verifikasi login. WebAuthn adalah API browser yang memungkinkan pengguna membuat kunci sandi dan mengautentikasi pengguna dengan kunci sandi. Untungnya, Anda sudah memiliki endpoint server yang merespons parameter tersebut dalam codelab ini.
- Untuk mendapatkan verifikasi login dan opsi lainnya dari endpoint server, tambahkan kode berikut ke isi fungsi
registerCredential()
setelah bagian komentar yang relevan:
public/client.js
// TODO: Add an ability to create a passkey: Obtain the challenge and other options from the server endpoint.
const options = await _fetch('/auth/registerRequest');
Cuplikan kode berikut menyertakan opsi contoh yang Anda terima dari server:
{
challenge: *****,
rp: {
id: "example.com",
},
user: {
id: *****,
name: "john78",
displayName: "John",
},
pubKeyCredParams: [{
alg: -7, type: "public-key"
},{
alg: -257, type: "public-key"
}],
excludeCredentials: [{
id: *****,
type: 'public-key',
transports: ['internal', 'hybrid'],
}],
authenticatorSelection: {
authenticatorAttachment: "platform",
requireResidentKey: true,
}
}
Protokol antara server dan klien bukan bagian dari spesifikasi WebAuthn. Namun, server codelab ini dirancang untuk menampilkan JSON yang sangat mirip dengan kamus PublicKeyCredentialCreationOptions
yang diteruskan ke API navigator.credentials.create()
WebAuthn.
Tabel berikut tidaklah lengkap, tetapi berisi parameter penting dalam kamus PublicKeyCredentialCreationOptions
:
Parameter | Deskripsi |
Verifikasi login yang dibuat server di objek | |
ID unik pengguna. Nilai ini harus berupa objek | |
Kolom ini harus berisi ID unik untuk akun yang dapat dikenali pengguna, seperti alamat email atau nama pengguna. ID akun ditampilkan di pemilih akun. (Jika Anda menggunakan nama pengguna, gunakan nilai yang sama dengan autentikasi sandi.) | |
Kolom ini merupakan nama opsional yang mudah digunakan untuk akun. Kolom ini tidak harus berupa nama yang unik, dan dapat berupa nama pilihan pengguna. Jika situs Anda tidak memiliki nilai yang sesuai untuk disertakan di sini, teruskan string kosong. Informasi ini mungkin ditampilkan di pemilih akun, bergantung pada browser. | |
ID pihak tepercaya (RP) adalah domain. Situs dapat menentukan domain atau akhiran yang dapat didaftarkan. Misalnya, jika asal RP adalah https://login.example.com:1337, ID RP dapat berupa | |
Kolom ini menentukan algoritma kunci publik yang didukung RP. Sebaiknya tetapkan ke | |
Memberikan daftar ID kredensial yang sudah terdaftar untuk mencegah pendaftaran perangkat yang sama dua kali. Jika disediakan, anggota | |
Tetapkan ke nilai | |
Tetapkan ke nilai | |
Tetapkan ke nilai |
Membuat kredensial
- Dalam isi fungsi
registerCredential()
setelah komentar yang relevan, konversikan beberapa parameter yang dienkode dengan Base64URL kembali ke biner, khususnya stringuser.id
danchallenge
, serta instance stringid
yang disertakan dalam arrayexcludeCredentials
:
public/client.js
// TODO: Add an ability to create a passkey: Create a credential.
// Base64URL decode some values.
options.user.id = base64url.decode(options.user.id);
options.challenge = base64url.decode(options.challenge);
if (options.excludeCredentials) {
for (let cred of options.excludeCredentials) {
cred.id = base64url.decode(cred.id);
}
}
- Pada baris berikutnya, tetapkan
authenticatorSelection.authenticatorAttachment
ke"platform"
danauthenticatorSelection.requireResidentKey
ketrue
. Hal ini hanya memungkinkan penggunaan pengautentikasi platform (perangkat itu sendiri) dengan kemampuan kredensial yang dapat ditemukan.
public/client.js
// Use platform authenticator and discoverable credential.
options.authenticatorSelection = {
authenticatorAttachment: 'platform',
requireResidentKey: true
}
- Pada baris berikutnya, panggil metode
navigator.credentials.create()
untuk membuat kredensial.
public/client.js
// Invoke the WebAuthn create() method.
const cred = await navigator.credentials.create({
publicKey: options,
});
Melalui panggilan ini, browser akan mencoba memverifikasi identitas pengguna dengan kunci layar perangkat.
Mendaftarkan kredensial ke endpoint server
Setelah pengguna memverifikasi identitasnya, kunci sandi akan dibuat dan disimpan. Situs menerima objek kredensial yang berisi kunci publik yang dapat Anda kirim ke server untuk mendaftarkan kunci sandi.
Cuplikan kode berikut berisi objek kredensial contoh:
{
"id": *****,
"rawId": *****,
"type": "public-key",
"response": {
"clientDataJSON": *****,
"attestationObject": *****,
"transports": ["internal", "hybrid"]
},
"authenticatorAttachment": "platform"
}
Tabel berikut tidaklah lengkap, tetapi berisi parameter penting dalam objek PublicKeyCredential
:
Parameter | Deskripsi |
ID yang dienkode Base64URL dari kunci sandi yang dibuat. ID ini membantu browser menentukan apakah kunci sandi yang cocok ada di perangkat saat autentikasi. Nilai ini harus disimpan pada database di backend. | |
Versi objek | |
Data klien yang dienkode objek | |
Objek pengesahan yang dienkode | |
Daftar transpor yang didukung perangkat: | |
Menampilkan |
Untuk mengirim objek kredensial ke server, ikuti langkah-langkah berikut:
- Lakukan enkode parameter biner kredensial sebagai Base64URL agar dapat dikirim ke server sebagai string:
public/client.js
// TODO: Add an ability to create a passkey: Register the credential to the server endpoint.
const credential = {};
credential.id = cred.id;
credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
credential.type = cred.type;
// The authenticatorAttachment string in the PublicKeyCredential object is a new addition in WebAuthn L3.
if (cred.authenticatorAttachment) {
credential.authenticatorAttachment = cred.authenticatorAttachment;
}
// Base64URL encode some values.
const clientDataJSON = base64url.encode(cred.response.clientDataJSON);
const attestationObject = base64url.encode(cred.response.attestationObject);
// Obtain transports.
const transports = cred.response.getTransports ? cred.response.getTransports() : [];
credential.response = {
clientDataJSON,
attestationObject,
transports
};
- Pada baris berikutnya, kirim objek ke server:
public/client.js
return await _fetch('/auth/registerResponse', credential);
Saat Anda menjalankan program, server akan menampilkan HTTP code 200
, yang menunjukkan bahwa kredensial tersebut terdaftar.
Sekarang Anda telah memiliki fungsi registerCredential()
lengkap.
Meninjau kode solusi untuk bagian ini
public/client.js
// TODO: Add an ability to create a passkey: Create the registerCredential() function.
export async function registerCredential() {
// TODO: Add an ability to create a passkey: Obtain the challenge and other options from server endpoint.
const options = await _fetch('/auth/registerRequest');
// TODO: Add an ability to create a passkey: Create a credential.
// Base64URL decode some values.
options.user.id = base64url.decode(options.user.id);
options.challenge = base64url.decode(options.challenge);
if (options.excludeCredentials) {
for (let cred of options.excludeCredentials) {
cred.id = base64url.decode(cred.id);
}
}
// Use platform authenticator and discoverable credential.
options.authenticatorSelection = {
authenticatorAttachment: 'platform',
requireResidentKey: true
}
// Invoke the WebAuthn create() method.
const cred = await navigator.credentials.create({
publicKey: options,
});
// TODO: Add an ability to create a passkey: Register the credential to the server endpoint.
const credential = {};
credential.id = cred.id;
credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
credential.type = cred.type;
// The authenticatorAttachment string in the PublicKeyCredential object is a new addition in WebAuthn L3.
if (cred.authenticatorAttachment) {
credential.authenticatorAttachment = cred.authenticatorAttachment;
}
// Base64URL encode some values.
const clientDataJSON = base64url.encode(cred.response.clientDataJSON);
const attestationObject =
base64url.encode(cred.response.attestationObject);
// Obtain transports.
const transports = cred.response.getTransports ?
cred.response.getTransports() : [];
credential.response = {
clientDataJSON,
attestationObject,
transports
};
return await _fetch('/auth/registerResponse', credential);
};
4. Membuat UI untuk mendaftarkan dan mengelola kredensial kunci sandi
Setelah fungsi registerCredential()
tersedia, Anda memerlukan tombol untuk memanggil fungsi tersebut. Selain itu, Anda perlu menampilkan daftar kunci sandi yang terdaftar.
Menambahkan HTML placeholder
- Di Glitch, buka file
views/home.html
. - Setelah komentar yang relevan, tambahkan placeholder UI yang menampilkan tombol untuk mendaftarkan sebuah kunci sandi dan daftar kunci sandi:
views/home.html
<!-- TODO: Add an ability to create a passkey: Add placeholder HTML. -->
<section>
<h3 class="mdc-typography mdc-typography--headline6"> Your registered
passkeys:</h3>
<div id="list"></div>
</section>
<p id="message" class="instructions"></p>
<mwc-button id="create-passkey" class="hidden" icon="fingerprint" raised>Create a passkey</mwc-button>
Elemen div#list
adalah placeholder untuk daftar.
Memeriksa dukungan kunci sandi
Untuk menampilkan opsi untuk membuat kunci sandi hanya kepada pengguna dengan perangkat yang mendukung kunci sandi, Anda harus memeriksa terlebih dahulu apakah WebAuthn tersedia atau tidak. Jika tersedia, Anda harus menghapus class hidden
untuk menampilkan tombol Buat kunci sandi.
Untuk memeriksa apakah lingkungan mendukung kunci sandi, ikuti langkah-langkah berikut:
- Di akhir file
views/home.html
setelah komentar yang relevan, tulis kondisional yang akan dijalankan jikawindow.PublicKeyCredential
,PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable
, danPublicKeyCredential.isConditionalMediationAvailable
adalahtrue
.
views/home.html
// TODO: Add an ability to create a passkey: Check for passkey support.
const createPasskey = $('#create-passkey');
// Feature detections
if (window.PublicKeyCredential &&
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable &&
PublicKeyCredential.isConditionalMediationAvailable) {
- Di bagian isi kondisional, periksa apakah perangkat dapat membuat kunci sandi, lalu periksa apakah kunci sandi dapat disarankan dalam isi otomatis formulir.
views/home.html
try {
const results = await Promise.all([
// Is platform authenticator available in this browser?
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(),
// Is conditional UI available in this browser?
PublicKeyCredential.isConditionalMediationAvailable()
]);
- Jika semua kondisi terpenuhi, tampilkan tombol untuk membuat kunci sandi. Jika tidak, tampilkan pesan peringatan.
views/home.html
if (results.every(r => r === true)) {
// If conditional UI is available, reveal the Create a passkey button.
createPasskey.classList.remove('hidden');
} else {
// If conditional UI isn't available, show a message.
$('#message').innerText = 'This device does not support passkeys.';
}
} catch (e) {
console.error(e);
}
} else {
// If WebAuthn isn't available, show a message.
$('#message').innerText = 'This device does not support passkeys.';
}
Merender kunci sandi terdaftar dalam daftar
- Tentukan fungsi
renderCredentials()
yang mengambil kunci sandi terdaftar dari server dan merendernya dalam daftar. Untungnya, Anda sudah memiliki endpoint server/auth/getKeys
untuk mengambil kunci sandi terdaftar bagi pengguna yang login.
views/home.html
// TODO: Add an ability to create a passkey: Render registered passkeys in a list.
async function renderCredentials() {
const res = await _fetch('/auth/getKeys');
const list = $('#list');
const creds = html`${res.length > 0 ? html`
<mwc-list>
${res.map(cred => html`
<mwc-list-item>
<div class="list-item">
<div class="entity-name">
<span>${cred.name || 'Unnamed' }</span>
</div>
<div class="buttons">
<mwc-icon-button data-cred-id="${cred.id}"
data-name="${cred.name || 'Unnamed' }" @click="${rename}"
icon="edit"></mwc-icon-button>
<mwc-icon-button data-cred-id="${cred.id}" @click="${remove}"
icon="delete"></mwc-icon-button>
</div>
</div>
</mwc-list-item>`)}
</mwc-list>` : html`
<mwc-list>
<mwc-list-item>No credentials found.</mwc-list-item>
</mwc-list>`}`;
render(creds, list);
};
- Pada baris berikutnya, panggil fungsi
renderCredentials()
untuk menampilkan kunci sandi terdaftar segera setelah pengguna membuka halaman/home
sebagai inisialisasi.
views/home.html
renderCredentials();
Membuat dan mendaftarkan kunci sandi
Untuk membuat dan mendaftarkan kunci sandi, Anda harus memanggil fungsi registerCredential()
yang telah Anda terapkan sebelumnya.
Untuk memicu fungsi registerCredential()
saat mengklik tombol Buat kunci sandi, ikuti langkah-langkah berikut:
- Di file setelah HTML placeholder, temukan pernyataan
import
berikut:
views/home.html
import {
$,
_fetch,
loading,
updateCredential,
unregisterCredential,
} from '/client.js';
- Di akhir isi pernyataan
import
, tambahkan fungsiregisterCredential()
.
views/home.html
// TODO: Add an ability to create a passkey: Create and register a passkey.
import {
$,
_fetch,
loading,
updateCredential,
unregisterCredential,
registerCredential
} from '/client.js';
- Di akhir file setelah komentar yang relevan, tentukan fungsi
register()
yang memanggil fungsiregisterCredential()
dan UI pemuatan, lalu panggilrenderCredentials()
setelah pendaftaran. Dengan ini, browser akan membuat kunci sandi dan menampilkan pesan error saat terjadi masalah.
views/home.html
// TODO: Add an ability to create a passkey: Create and register a passkey.
async function register() {
try {
// Start the loading UI.
loading.start();
// Start creating a passkey.
await registerCredential();
// Stop the loading UI.
loading.stop();
// Render the updated passkey list.
renderCredentials();
- Dalam isi fungsi
register()
, tangkap pengecualian. Metodenavigator.credentials.create()
akan menampilkan errorInvalidStateError
jika kunci sandi sudah ada di perangkat. Langkah ini diperiksa dengan arrayexcludeCredentials
. Dalam kasus ini, Anda menampilkan pesan yang relevan kepada pengguna. Tindakan ini juga menampilkan errorNotAllowedError
jika pengguna membatalkan dialog autentikasi. Dalam kasus ini, Anda diam-diam mengabaikannya.
views/home.html
} catch (e) {
// Stop the loading UI.
loading.stop();
// An InvalidStateError indicates that a passkey already exists on the device.
if (e.name === 'InvalidStateError') {
alert('A passkey already exists for this device.');
// A NotAllowedError indicates that the user canceled the operation.
} else if (e.name === 'NotAllowedError') {
Return;
// Show other errors in an alert.
} else {
alert(e.message);
console.error(e);
}
}
};
- Pada baris setelah fungsi
register()
, lampirkan fungsiregister()
ke peristiwaclick
untuk tombol Buat kunci sandi.
views/home.html
createPasskey.addEventListener('click', register);
Meninjau kode solusi untuk bagian ini
views/home.html
<!-- TODO: Add an ability to create a passkey: Add placeholder HTML. -->
<section>
<h3 class="mdc-typography mdc-typography--headline6"> Your registered
passkeys:</h3>
<div id="list"></div>
</section>
<p id="message" class="instructions"></p>
<mwc-button id="create-passkey" class="hidden" icon="fingerprint" raised>Create a passkey</mwc-button>
views/home.html
// TODO: Add an ability to create a passkey: Create and register a passkey.
import {
$,
_fetch,
loading,
updateCredential,
unregisterCredential,
registerCredential
} from '/client.js';
views/home.html
// TODO: Add an ability to create a passkey: Check for passkey support.
const createPasskey = $('#create-passkey');
// Feature detections
if (window.PublicKeyCredential &&
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable &&
PublicKeyCredential.isConditionalMediationAvailable) {
try {
const results = await Promise.all([
// Is platform authenticator available in this browser?
PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(),
// Is conditional UI available in this browser?
PublicKeyCredential.isConditionalMediationAvailable()
]);
if (results.every(r => r === true)) {
// If conditional UI is available, reveal the Create a passkey button.
createPasskey.classList.remove('hidden');
} else {
// If conditional UI isn't available, show a message.
$('#message').innerText = 'This device does not support passkeys.';
}
} catch (e) {
console.error(e);
}
} else {
// If WebAuthn isn't available, show a message.
$('#message').innerText = 'This device does not support passkeys.';
}
// TODO: Add an ability to create a passkey: Render registered passkeys in a list.
async function renderCredentials() {
const res = await _fetch('/auth/getKeys');
const list = $('#list');
const creds = html`${res.length > 0 ? html`
<mwc-list>
${res.map(cred => html`
<mwc-list-item>
<div class="list-item">
<div class="entity-name">
<span>${cred.name || 'Unnamed' }</span>
</div>
<div class="buttons">
<mwc-icon-button data-cred-id="${cred.id}" data-name="${cred.name || 'Unnamed' }" @click="${rename}" icon="edit"></mwc-icon-button>
<mwc-icon-button data-cred-id="${cred.id}" @click="${remove}" icon="delete"></mwc-icon-button>
</div>
</div>
</mwc-list-item>`)}
</mwc-list>` : html`
<mwc-list>
<mwc-list-item>No credentials found.</mwc-list-item>
</mwc-list>`}`;
render(creds, list);
};
renderCredentials();
// TODO: Add an ability to create a passkey: Create and register a passkey.
async function register() {
try {
// Start the loading UI.
loading.start();
// Start creating a passkey.
await registerCredential();
// Stop the loading UI.
loading.stop();
// Render the updated passkey list.
renderCredentials();
} catch (e) {
// Stop the loading UI.
loading.stop();
// An InvalidStateError indicates that a passkey already exists on the device.
if (e.name === 'InvalidStateError') {
alert('A passkey already exists for this device.');
// A NotAllowedError indicates that the user canceled the operation.
} else if (e.name === 'NotAllowedError') {
Return;
// Show other errors in an alert.
} else {
alert(e.message);
console.error(e);
}
}
};
createPasskey.addEventListener('click', register);
Cobalah
Jika sudah mengikuti semua langkah sejauh ini, coba terapkan kemampuan untuk membuat, mendaftarkan, dan menampilkan kunci sandi di situs.
Untuk mencobanya, ikuti langkah-langkah berikut:
- Di tab pratinjau, login dengan nama pengguna dan sandi acak.
- Klik Buat kunci sandi.
- Verifikasi identitas Anda dengan kunci layar perangkat.
- Pastikan kunci sandi terdaftar dan muncul di bagian Kunci sandi terdaftar dari halaman web.
Mengganti nama dan menghapus kunci sandi terdaftar
Anda seharusnya dapat mengganti nama atau menghapus kunci sandi terdaftar dalam daftar. Anda dapat memeriksa cara kerjanya dalam kode yang disertakan dengan codelab.
Di Chrome, Anda dapat menghapus kunci sandi terdaftar dari chrome://settings/passkeys di desktop atau dari pengelola sandi di setelan pada Android.
Untuk informasi tentang cara mengganti nama dan menghapus kunci sandi terdaftar di platform lain, lihat masing-masing halaman dukungan untuk platform tersebut.
5. Menambahkan kemampuan untuk melakukan autentikasi dengan kunci sandi
Pengguna kini dapat membuat dan mendaftarkan kunci sandi, serta dapat menggunakannya sebagai cara untuk melakukan autentikasi ke situs dengan aman. Sekarang Anda perlu menambahkan kemampuan autentikasi kunci sandi ke situs.
Membuat fungsi authenticate()
- Di file
public/client.js
setelah komentar yang relevan, buat fungsi bernamaauthenticate()
yang memverifikasi pengguna dan server secara lokal:
public/client.js
// TODO: Add an ability to authenticate with a passkey: Create the authenticate() function.
export async function authenticate() {
// TODO: Add an ability to authenticate with a passkey: Obtain the challenge and other options from the server endpoint.
// TODO: Add an ability to authenticate with a passkey: Locally verify the user and get a credential.
// TODO: Add an ability to authenticate with a passkey: Verify the credential.
};
Mendapatkan verifikasi login dan opsi lainnya dari endpoint server
Sebelum meminta pengguna untuk mengautentikasi, Anda harus meminta parameter untuk meneruskan WebAuthn dari server, termasuk verifikasi login.
- Dalam isi fungsi
authenticate()
setelah komentar yang relevan, panggil fungsi_fetch()
untuk mengirim permintaanPOST
ke server:
public/client.js
// TODO: Add an ability to authenticate with a passkey: Obtain the challenge and other options from the server endpoint.
const options = await _fetch('/auth/signinRequest');
Server codelab ini dirancang untuk menampilkan JSON yang sangat mirip dengan kamus PublicKeyCredentialRequestOptions
yang diteruskan ke API navigator.credentials.get()
WebAuthn. Cuplikan kode berikut menyertakan opsi contoh yang harus Anda terima:
{
"challenge": *****,
"rpId": "passkeys-codelab.glitch.me",
"allowCredentials": []
}
Tabel berikut tidaklah lengkap, tetapi berisi parameter penting dalam kamus PublicKeyCredentialRequestOptions
:
Parameter | Deskripsi |
Verifikasi login yang dibuat server di objek | |
ID RP adalah domain. Situs dapat menentukan domain atau akhiran yang dapat didaftarkan. Nilai ini harus cocok dengan parameter | |
Properti ini digunakan untuk menemukan pengautentikasi yang memenuhi syarat untuk autentikasi ini. Teruskan array kosong atau biarkan tidak ditentukan agar browser menampilkan pemilih akun. | |
Tetapkan ke nilai |
Memverifikasi pengguna secara lokal dan mendapatkan kredensial
- Dalam isi fungsi
authenticate()
setelah komentar yang relevan, konversikan parameterchallenge
kembali ke biner:
public/client.js
// TODO: Add an ability to authenticate with a passkey: Locally verify the user and get a credential.
// Base64URL decode the challenge.
options.challenge = base64url.decode(options.challenge);
- Teruskan array kosong ke parameter
allowCredentials
untuk membuka pemilih akun saat pengguna melakukan autentikasi:
public/client.js
// An empty allowCredentials array invokes an account selector by discoverable credentials.
options.allowCredentials = [];
Pemilih akun menggunakan informasi pengguna yang disimpan dengan kunci sandi.
- Panggil metode
navigator.credentials.get()
bersama dengan opsimediation: 'conditional'
:
public/client.js
// Invoke the WebAuthn get() method.
const cred = await navigator.credentials.get({
publicKey: options,
// Request a conditional UI.
mediation: 'conditional'
});
Opsi ini menginstruksikan browser agar menyarankan kunci sandi secara kondisional sebagai bagian dari isi otomatis formulir.
Memverifikasi kredensial
Setelah pengguna memverifikasi identitasnya secara lokal, Anda akan menerima objek kredensial berisi tanda tangan yang dapat diverifikasi di server.
Cuplikan kode berikut menyertakan contoh objek PublicKeyCredential
:
{
"id": *****,
"rawId": *****,
"type": "public-key",
"response": {
"clientDataJSON": *****,
"authenticatorData": *****,
"signature": *****,
"userHandle": *****
},
authenticatorAttachment: "platform"
}
Tabel berikut tidaklah lengkap, tetapi berisi parameter penting dalam objek PublicKeyCredential
:
Parameter | Deskripsi |
ID yang dienkode Base64URL dari kredensial kunci sandi yang diautentikasi. | |
Versi objek | |
Objek | |
Objek | |
Objek | |
Objek | |
Tampilkan string |
Untuk mengirim objek kredensial ke server, ikuti langkah-langkah berikut:
- Dalam isi fungsi
authenticate()
setelah komentar yang relevan, lakukan enkode parameter biner kredensial agar parameter tersebut dapat dikirim ke server sebagai string:
public/client.js
// TODO: Add an ability to authenticate with a passkey: Verify the credential.
const credential = {};
credential.id = cred.id;
credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
credential.type = cred.type;
// Base64URL encode some values.
const clientDataJSON = base64url.encode(cred.response.clientDataJSON);
const authenticatorData = base64url.encode(cred.response.authenticatorData);
const signature = base64url.encode(cred.response.signature);
const userHandle = base64url.encode(cred.response.userHandle);
credential.response = {
clientDataJSON,
authenticatorData,
signature,
userHandle,
};
- Kirim objek ke server:
public/client.js
return await _fetch(`/auth/signinResponse`, credential);
Saat Anda menjalankan program, server akan menampilkan HTTP code 200
, yang menunjukkan bahwa kredensial tersebut telah diverifikasi.
Anda sekarang memiliki fungsi authentication()
lengkap.
Meninjau kode solusi untuk bagian ini
public/client.js
// TODO: Add an ability to authenticate with a passkey: Create the authenticate() function.
export async function authenticate() {
// TODO: Add an ability to authenticate with a passkey: Obtain the
challenge and other options from the server endpoint.
const options = await _fetch('/auth/signinRequest');
// TODO: Add an ability to authenticate with a passkey: Locally verify
the user and get a credential.
// Base64URL decode the challenge.
options.challenge = base64url.decode(options.challenge);
// The empty allowCredentials array invokes an account selector
by discoverable credentials.
options.allowCredentials = [];
// Invoke the WebAuthn get() function.
const cred = await navigator.credentials.get({
publicKey: options,
// Request a conditional UI.
mediation: 'conditional'
});
// TODO: Add an ability to authenticate with a passkey: Verify the credential.
const credential = {};
credential.id = cred.id;
credential.rawId = cred.id; // Pass a Base64URL encoded ID string.
credential.type = cred.type;
// Base64URL encode some values.
const clientDataJSON = base64url.encode(cred.response.clientDataJSON);
const authenticatorData =
base64url.encode(cred.response.authenticatorData);
const signature = base64url.encode(cred.response.signature);
const userHandle = base64url.encode(cred.response.userHandle);
credential.response = {
clientDataJSON,
authenticatorData,
signature,
userHandle,
};
return await _fetch(`/auth/signinResponse`, credential);
};
6. Menambahkan kunci sandi ke isi otomatis browser
Saat pengguna kembali, buat pengguna login semudah dan seaman mungkin. Jika Anda menambahkan tombol Login dengan kunci sandi ke halaman login, pengguna dapat menekan tombol tersebut, memilih kunci sandi di pemilih akun browser, dan menggunakan kunci layar untuk memverifikasi identitas.
Namun, peralihan dari sandi ke kunci sandi tidak dapat dilakukan untuk semua pengguna secara sekaligus. Artinya, Anda tidak dapat menghapus sandi sampai semua pengguna beralih ke kunci sandi. Anda harus menutup formulir login berbasis sandi sampai proses tersebut selesai. Namun, jika Anda menutup formulir sandi dan tombol kunci sandi, pengguna harus memilih di antara kedua cara tersebut untuk login, yang seharusnya tidak diperlukan. Idealnya, buat proses login yang mudah.
Di sinilah UI kondisional dapat membantu. UI kondisional adalah fitur WebAuthn yang memungkinkan Anda membuat kolom input formulir untuk menyarankan kunci sandi sebagai bagian dari item isi otomatis selain sandi. Jika pengguna mengetuk kunci sandi dalam saran isi otomatis, pengguna akan diminta menggunakan kunci layar perangkat untuk memverifikasi identitasnya secara lokal. Hal ini adalah pengalaman pengguna yang lancar karena tindakan login pengguna hampir sama dengan login berbasis sandi.
Mengaktifkan UI kondisional
Untuk mengaktifkan UI kondisional, Anda hanya perlu menambahkan token webauthn
dalam atribut autocomplete
kolom input. Setelah token ditetapkan, Anda dapat memanggil metode navigator.credentials.get()
dengan string mediation: 'conditional'
untuk memicu UI kunci layar secara kondisional.
- Untuk mengaktifkan UI kondisional, ganti kolom input nama pengguna yang ada dengan HTML berikut setelah komentar yang relevan dalam file
view/index.html
:
view/index.html
<!-- TODO: Add passkeys to the browser autofill: Enable conditional UI. -->
<input
type="text"
id="username"
class="mdc-text-field__input"
aria-labelledby="username-label"
name="username"
autocomplete="username webauthn"
autofocus />
Mendeteksi fitur, memanggil WebAuthn, dan mengaktifkan UI kondisional
- Di file
view/index.html
setelah komentar yang relevan, ganti pernyataanimport
yang ada dengan kode berikut:
view/index.html
// TODO: Add passkeys to the browser autofill: Detect features, invoke WebAuthn, and enable a conditional UI.
import {
$,
_fetch,
loading,
authenticate
} from "/client.js";
Kode ini mengimpor fungsi authenticate()
yang Anda telah terapkan sebelumnya.
- Pastikan objek
window.PulicKeyCredential
tersedia dan metodePublicKeyCredential.isConditionalMediationAvailable()
menampilkan nilaitrue
, lalu panggil fungsiauthenticate()
:
view/index.html
// TODO: Add passkeys to the browser autofill: Detect features, invoke WebAuthn, and enable a conditional UI.
if (
window.PublicKeyCredential &&
PublicKeyCredential.isConditionalMediationAvailable
) {
try {
// Is conditional UI available in this browser?
const cma =
await PublicKeyCredential.isConditionalMediationAvailable();
if (cma) {
// If conditional UI is available, invoke the authenticate() function.
const user = await authenticate();
if (user) {
// Proceed only when authentication succeeds.
$("#username").value = user.username;
loading.start();
location.href = "/home";
} else {
throw new Error("User not found.");
}
}
} catch (e) {
loading.stop();
// A NotAllowedError indicates that the user canceled the operation.
if (e.name !== "NotAllowedError") {
console.error(e);
alert(e.message);
}
}
}
Meninjau kode solusi untuk bagian ini
view/index.html
<!-- TODO: Add passkeys to the browser autofill: Enable conditional UI. -->
<input
type="text"
id="username"
class="mdc-text-field__input"
aria-labelledby="username-label"
name="username"
autocomplete="username webauthn"
autofocus
/>
view/index.html
// TODO: Add passkeys to the browser autofill: Detect features, invoke WebAuthn, and enable a conditional UI.
import {
$,
_fetch,
loading,
authenticate
} from '/client.js';
view/index.html
// TODO: Add passkeys to the browser autofill: Detect features, invoke WebAuthn, and enable a conditional UI.
// Is WebAuthn avaiable in this browser?
if (window.PublicKeyCredential &&
PublicKeyCredential.isConditionalMediationAvailable) {
try {
// Is a conditional UI available in this browser?
const cma= await PublicKeyCredential.isConditionalMediationAvailable();
if (cma) {
// If a conditional UI is available, invoke the authenticate() function.
const user = await authenticate();
if (user) {
// Proceed only when authentication succeeds.
$('#username').value = user.username;
loading.start();
location.href = '/home';
} else {
throw new Error('User not found.');
}
}
} catch (e) {
loading.stop();
// A NotAllowedError indicates that the user canceled the operation.
if (e.name !== 'NotAllowedError') {
console.error(e);
alert(e.message);
}
}
}
Cobalah
Coba buat, daftarkan, tampilkan, dan lakukan autentikasi kunci sandi di situs Anda.
Untuk mencobanya, ikuti langkah-langkah berikut:
- Buka tab pratinjau.
- Jika perlu, logout.
- Klik kotak teks nama pengguna. Dialog akan muncul.
- Pilih akun yang ingin Anda gunakan untuk login.
- Verifikasi identitas Anda dengan kunci layar perangkat. Anda akan dialihkan ke halaman
/home
dan login.
7. Selamat!
Anda telah menyelesaikan codelab ini. Jika ada pertanyaan, kirimkan ke milis FIDO-DEV atau di StackOverflow dengan tag passkey
.