Ringkasan
Berikut ringkasan umum langkah-langkah utama yang terlibat dalam pendaftaran kunci sandi:
- Tentukan opsi untuk membuat kunci sandi. Kirimkan ke klien, sehingga Anda dapat meneruskannya ke panggilan pembuatan kunci sandi: panggilan WebAuthn API
navigator.credentials.create
di web, dancredentialManager.createCredential
di Android. Setelah pengguna mengonfirmasi pembuatan kunci sandi, panggilan pembuatan kunci sandi akan diselesaikan dan menampilkanPublicKeyCredential
kredensial. - Verifikasi kredensial dan simpan di server.
Bagian berikut membahas secara mendalam spesifikasi setiap langkah.
Membuat opsi pembuatan kredensial
Langkah pertama yang perlu Anda lakukan di server adalah membuat objek PublicKeyCredentialCreationOptions
.
Untuk melakukannya, gunakan library sisi server FIDO Anda. Biasanya, fungsi ini akan menawarkan fungsi utilitas yang dapat membuat opsi ini untuk Anda. SimpleWebAuthn menawarkan, misalnya, generateRegistrationOptions
.
PublicKeyCredentialCreationOptions
harus mencakup semua yang diperlukan untuk pembuatan kunci sandi: informasi tentang pengguna, tentang RP, dan konfigurasi untuk properti kredensial yang Anda buat. Setelah Anda menentukan semuanya, teruskan sesuai kebutuhan ke fungsi di library sisi server FIDO yang bertanggung jawab untuk membuat objek PublicKeyCredentialCreationOptions
.
Beberapa kolom PublicKeyCredentialCreationOptions
' dapat berupa konstanta. Lainnya harus ditentukan secara dinamis di server:
rpId
: Untuk mengisi ID RP di server, gunakan fungsi atau variabel sisi server yang memberi Anda nama host aplikasi web, sepertiexample.com
.user.name
danuser.displayName
: Untuk mengisi kolom ini, gunakan informasi sesi pengguna yang login (atau informasi akun pengguna baru, jika pengguna membuat kunci sandi saat mendaftar).user.name
biasanya berupa alamat email, dan unik untuk RP.user.displayName
adalah nama yang mudah digunakan. Perhatikan bahwa tidak semua platform akan menggunakandisplayName
.user.id
: String unik acak yang dibuat saat pembuatan akun. ID ini harus permanen, tidak seperti nama pengguna yang dapat diedit. ID pengguna mengidentifikasi akun, tetapi tidak boleh berisi informasi identitas pribadi (PII). Anda mungkin sudah memiliki ID pengguna di sistem Anda, tetapi jika diperlukan, buat ID khusus untuk kunci sandi agar tidak berisi PII.excludeCredentials
: Daftar ID kredensial yang ada untuk mencegah duplikasi kunci sandi dari penyedia kunci sandi. Untuk mengisi kolom ini, cari kredensial yang ada untuk pengguna ini di database Anda. Tinjau detailnya di Mencegah pembuatan kunci sandi baru jika sudah ada.challenge
: Untuk pendaftaran kredensial, tantangan tidak relevan kecuali jika Anda menggunakan pengesahan, teknik yang lebih canggih untuk memverifikasi identitas penyedia kunci sandi dan data yang dikeluarkannya. Namun, meskipun Anda tidak menggunakan pengesahan, tantangan tetap merupakan kolom wajib diisi. Petunjuk untuk membuat tantangan yang aman untuk autentikasi tersedia di Autentikasi kunci sandi sisi server.
Encoding dan decoding

PublicKeyCredentialCreationOptions
yang dikirim oleh server. challenge
, user.id
, dan excludeCredentials.credentials
harus dienkode di sisi server menjadi base64URL
, sehingga PublicKeyCredentialCreationOptions
dapat dikirimkan melalui HTTPS.PublicKeyCredentialCreationOptions
menyertakan kolom yang berupa ArrayBuffer
, sehingga tidak didukung oleh JSON.stringify()
. Artinya, saat ini, untuk mengirimkan PublicKeyCredentialCreationOptions
melalui HTTPS, beberapa kolom harus dienkode secara manual di server menggunakan base64URL
, lalu didekode di klien.
- Di server, encoding dan decoding biasanya ditangani oleh library sisi server FIDO Anda.
- Di klien, encoding dan decoding harus dilakukan secara manual untuk saat ini. Hal ini akan menjadi lebih mudah pada masa mendatang: metode untuk mengonversi opsi sebagai JSON ke dalam
PublicKeyCredentialCreationOptions
akan tersedia. Periksa status penerapan di Chrome.
Contoh kode: membuat opsi pembuatan kredensial
Kita akan menggunakan library SimpleWebAuthn dalam contoh kita. Di sini, kita menyerahkan pembuatan opsi kredensial kunci publik ke fungsi generateRegistrationOptions
-nya.
import {
generateRegistrationOptions,
verifyRegistrationResponse,
generateAuthenticationOptions,
verifyAuthenticationResponse
} from '@simplewebauthn/server';
import { isoBase64URL } from '@simplewebauthn/server/helpers';
router.post('/registerRequest', csrfCheck, sessionCheck, async (req, res) => {
const { user } = res.locals;
// Ensure you nest verification function calls in try/catch blocks.
// If something fails, throw an error with a descriptive error message.
// Return that message with an appropriate error code to the client.
try {
// `excludeCredentials` prevents users from re-registering existing
// credentials for a given passkey provider
const excludeCredentials = [];
const credentials = Credentials.findByUserId(user.id);
if (credentials.length > 0) {
for (const cred of credentials) {
excludeCredentials.push({
id: isoBase64URL.toBuffer(cred.id),
type: 'public-key',
transports: cred.transports,
});
}
}
// Generate registration options for WebAuthn create
const options = await generateRegistrationOptions({
rpName: process.env.RP_NAME,
rpID: process.env.HOSTNAME,
userID: user.id,
userName: user.username,
userDisplayName: user.displayName || '',
attestationType: 'none',
excludeCredentials,
authenticatorSelection: {
authenticatorAttachment: 'platform',
requireResidentKey: true
},
});
// Keep the challenge in the session
req.session.challenge = options.challenge;
return res.json(options);
} catch (e) {
console.error(e);
return res.status(400).send({ error: e.message });
}
});
Simpan kunci publik

navigator.credentials.create
menampilkan objek PublicKeyCredential
.Jika navigator.credentials.create
berhasil di-resolve di klien, berarti kunci sandi telah berhasil dibuat. Objek PublicKeyCredential
ditampilkan.
Objek PublicKeyCredential
berisi objek AuthenticatorAttestationResponse
, yang merepresentasikan respons penyedia kunci sandi terhadap perintah klien untuk membuat kunci sandi. Objek ini berisi informasi tentang kredensial baru yang Anda perlukan sebagai RP untuk mengautentikasi pengguna nanti. Pelajari lebih lanjut AuthenticatorAttestationResponse
di Lampiran: AuthenticatorAttestationResponse
.
Kirim objek PublicKeyCredential
ke server. Setelah Anda menerimanya, verifikasi kartu tersebut.
Serahkan langkah verifikasi ini ke library sisi server FIDO Anda. Biasanya, fungsi utilitas akan ditawarkan untuk tujuan ini. SimpleWebAuthn menawarkan, misalnya, verifyRegistrationResponse
. Pelajari apa yang terjadi di balik layar dalam Lampiran: verifikasi respons pendaftaran.
Setelah verifikasi berhasil, simpan informasi kredensial di database Anda sehingga pengguna dapat melakukan autentikasi nanti dengan kunci sandi yang terkait dengan kredensial tersebut.
Gunakan tabel khusus untuk kredensial kunci publik yang terkait dengan kunci sandi. Pengguna hanya dapat memiliki satu sandi, tetapi dapat memiliki beberapa kunci sandi — misalnya, kunci sandi yang disinkronkan melalui Apple iCloud Keychain dan satu lagi melalui Pengelola Sandi Google.
Berikut adalah contoh skema yang dapat Anda gunakan untuk menyimpan informasi kredensial:
- Tabel Pengguna:
user_id
: ID pengguna utama. ID acak, unik, dan permanen untuk pengguna. Gunakan ini sebagai kunci utama untuk tabel Users Anda.username
. Nama pengguna yang ditentukan pengguna, yang mungkin dapat diedit.passkey_user_id
: ID pengguna bebas PII khusus kunci sandi, yang diwakili olehuser.id
dalam opsi pendaftaran Anda. Saat pengguna mencoba melakukan autentikasi nanti, pengautentikasi akan menyediakanpasskey_user_id
ini dalam respons autentikasinya diuserHandle
. Sebaiknya jangan tetapkanpasskey_user_id
sebagai kunci utama. Kunci utama cenderung menjadi PII de facto dalam sistem, karena digunakan secara ekstensif.
- Tabel Public key credentials:
id
: ID kredensial. Gunakan ini sebagai kunci utama untuk tabel Kredensial kunci publik Anda.public_key
: Kunci publik kredensial.passkey_user_id
: Gunakan ini sebagai kunci asing untuk membuat link dengan tabel Pengguna.backed_up
: Kunci sandi dicadangkan jika disinkronkan oleh penyedia kunci sandi. Menyimpan status cadangan berguna jika Anda ingin mempertimbangkan untuk tidak menggunakan sandi di masa mendatang bagi pengguna yang memiliki kunci sandibacked_up
. Anda dapat memeriksa apakah kunci sandi dicadangkan dengan memeriksa tanda BE diauthenticatorData
, atau dengan menggunakan fitur library sisi server FIDO yang biasanya tersedia untuk memberi Anda akses mudah ke informasi ini. Menyimpan kelayakan pencadangan dapat membantu menjawab potensi pertanyaan pengguna.name
: Opsional, nama tampilan untuk kredensial agar pengguna dapat memberikan nama kustom pada kredensial.transports
: Array transportasi. Menyimpan transportasi berguna untuk pengalaman pengguna autentikasi. Jika transport tersedia, browser dapat berperilaku sesuai dan menampilkan UI yang cocok dengan transport yang digunakan penyedia kunci sandi untuk berkomunikasi dengan klien—khususnya untuk kasus penggunaan autentikasi ulang saatallowCredentials
tidak kosong.
Informasi lain dapat berguna untuk disimpan demi tujuan pengalaman pengguna, termasuk item seperti penyedia kunci sandi, waktu pembuatan kredensial, dan waktu terakhir digunakan. Baca selengkapnya di Desain antarmuka pengguna kunci sandi.
Contoh kode: menyimpan kredensial
Kita akan menggunakan library SimpleWebAuthn dalam contoh kita.
Di sini, kita menyerahkan verifikasi respons pendaftaran ke fungsi verifyRegistrationResponse
-nya.
import { isoBase64URL } from '@simplewebauthn/server/helpers';
router.post('/registerResponse', csrfCheck, sessionCheck, async (req, res) => {
const expectedChallenge = req.session.challenge;
const expectedOrigin = getOrigin(req.get('User-Agent'));
const expectedRPID = process.env.HOSTNAME;
const response = req.body;
// This sample code is for registering a passkey for an existing,
// signed-in user
// Ensure you nest verification function calls in try/catch blocks.
// If something fails, throw an error with a descriptive error message.
// Return that message with an appropriate error code to the client.
try {
// Verify the credential
const { verified, registrationInfo } = await verifyRegistrationResponse({
response,
expectedChallenge,
expectedOrigin,
expectedRPID,
requireUserVerification: false,
});
if (!verified) {
throw new Error('Verification failed.');
}
const {
aaguid,
credentialPublicKey,
credentialID,
credentialBackedUp
} = registrationInfo;
// Name the credential based on AAGUID
const name =
aaguid === undefined ||
aaguid === '000000-0000-0000-0000-00000000' ?
req.useragent?.platform : aaguids[aaguid].name;
const base64CredentialID = isoBase64URL.fromBuffer(credentialID);
const base64PublicKey = isoBase64URL.fromBuffer(credentialPublicKey);
// Existing, signed-in user
const { user } = res.locals;
// Save the credential
await Credentials.update({
id: base64CredentialID,
passkey_user_id: user.passkey_user_id,
publicKey: base64PublicKey,
name,
aaguid,
transports: response.response.transports,
backed_up: credentialBackedUp,
registered_at: new Date().getTime()
});
// Kill the challenge for this session
delete req.session.challenge;
return res.json(user);
} catch (e) {
delete req.session.challenge;
console.error(e);
return res.status(400).send({ error: e.message });
}
});
Lampiran: AuthenticatorAttestationResponse
AuthenticatorAttestationResponse
berisi dua objek penting:
response.clientDataJSON
adalah versi JSON dari data klien, yang di web adalah data sebagaimana yang dilihat oleh browser. Objek ini berisi asal RP, verifikasi login, danandroidPackageName
jika klien adalah aplikasi Android. Sebagai RP, membacaclientDataJSON
memberi Anda akses ke informasi yang dilihat browser pada saat permintaancreate
.response.attestationObject
berisi dua bagian informasi:attestationStatement
yang tidak relevan kecuali jika Anda menggunakan pengesahan.authenticatorData
adalah data sebagaimana yang dilihat oleh penyedia kunci sandi. Sebagai RP, membacaauthenticatorData
memberi Anda akses ke data yang dilihat oleh penyedia kunci sandi dan ditampilkan pada saat permintaancreate
.
authenticatorData
berisi informasi penting tentang kredensial kunci publik yang terkait dengan kunci sandi yang baru dibuat:
- Kredensial kunci publik itu sendiri, dan ID kredensial unik untuknya.
- ID RP yang terkait dengan kredensial.
- Flag yang menjelaskan status pengguna saat kunci sandi dibuat: apakah pengguna benar-benar hadir, dan apakah pengguna berhasil diverifikasi (lihat pembahasan mendalam verifikasi pengguna).
- AAGUID adalah ID untuk penyedia kunci sandi seperti Pengelola Sandi Google. Berdasarkan AAGUID, Anda dapat mengidentifikasi penyedia kunci sandi dan menampilkan namanya di halaman pengelolaan kunci sandi. (lihat Menentukan penyedia kunci sandi dengan AAGUID)
Meskipun authenticatorData
berada di dalam attestationObject
, informasi yang dikandungnya diperlukan untuk penerapan kunci sandi Anda, baik Anda menggunakan pengesahan atau tidak. authenticatorData
dienkode, dan berisi kolom yang dienkode dalam format biner. Library sisi server Anda biasanya akan menangani penguraian dan decoding. Jika Anda tidak menggunakan library sisi server, pertimbangkan untuk memanfaatkan sisi klien getAuthenticatorData()
untuk menghemat beberapa pekerjaan parsing dan decoding sisi server.
Lampiran: verifikasi respons pendaftaran
Di balik layar, memverifikasi respons pendaftaran terdiri dari pemeriksaan berikut:
- Pastikan ID RP cocok dengan situs Anda.
- Pastikan bahwa asal permintaan adalah asal yang diharapkan untuk situs Anda (URL situs utama, aplikasi Android).
- Jika Anda mewajibkan verifikasi pengguna, pastikan tanda verifikasi pengguna
authenticatorData.uv
adalahtrue
. - Flag kehadiran pengguna
authenticatorData.up
biasanya diharapkan bernilaitrue
, tetapi jika kredensial dibuat secara bersyarat, nilai yang diharapkan adalahfalse
. - Periksa apakah klien dapat memberikan tantangan yang Anda berikan. Jika Anda tidak menggunakan pengesahan, pemeriksaan ini tidak penting. Namun, menerapkan pemeriksaan ini adalah praktik terbaik: hal ini memastikan kode Anda siap jika Anda memutuskan untuk menggunakan pengesahan di masa mendatang.
- Pastikan ID kredensial belum terdaftar untuk pengguna mana pun.
- Verifikasi bahwa algoritma yang digunakan oleh penyedia kunci sandi untuk membuat kredensial adalah algoritma yang Anda cantumkan (di setiap kolom
alg
daripublicKeyCredentialCreationOptions.pubKeyCredParams
, yang biasanya ditentukan dalam library sisi server Anda dan tidak terlihat oleh Anda). Hal ini memastikan bahwa pengguna hanya dapat mendaftar dengan algoritma yang telah Anda pilih untuk diizinkan.
Untuk mempelajari lebih lanjut, periksa kode sumber verifyRegistrationResponse
SimpleWebAuthn atau pelajari daftar lengkap verifikasi dalam spesifikasi.