# Passkey di Next.js: Autentikasi Tanpa Kata Sandi dengan WebAuthn

> Panduan praktis menambahkan login passkey ke aplikasi Next.js: proses registrasi dan autentikasi WebAuthn, penyimpanan kredensial yang aman, serta opsi cadangan bagi pengguna yang perangkatnya belum mendukung passkey.

URL: https://www.matthewswong.com/id/blog/passkeys-webauthn-nextjs-authentication/

- Author: Matthews Wong (https://www.matthewswong.com/id/about/)
- Published: 2026-07-05
- Updated: 2026-07-05
- Category: Security

Kata sandi adalah titik terlemah di hampir setiap sistem autentikasi yang pernah saya bangun. Pengguna memakai ulang kata sandi yang sama, halaman phishing memanennya, dan setiap alur reset password berujung menjadi tiket support baru. Passkey menyelesaikan akar masalah ini, bukan sekadar menambalnya: passkey menggantikan rahasia bersama dengan pasangan kunci publik yang dibuat dan dikunci ke autentikator perangkat, sehingga tidak ada apa pun di database Anda yang bisa dicuri penyerang untuk login di tempat lain.

Tulisan ini membahas cara menambahkan passkey ke proyek Next.js App Router menggunakan API browser WebAuthn, yang dibungkus oleh library SimpleWebAuthn agar Anda tidak perlu menulis sendiri parsing CBOR dan verifikasi attestation. Saya akan membahas proses registrasi dan login, apa yang perlu disimpan di server, bagaimana pengecekan challenge dan origin benar-benar melindungi Anda, serta opsi cadangan untuk sebagian kecil pengguna dengan browser lama atau perangkat kantor yang dikunci kebijakan.

## Apa sebenarnya passkey itu

Passkey adalah nama yang digunakan pengguna untuk menyebut kredensial WebAuthn: pasangan kunci asimetris di mana kunci privat tidak pernah meninggalkan autentikator, baik itu secure enclave di ponsel, autentikator platform di laptop, atau security key fisik. Browser dan sistem operasi yang menangani kriptografinya; server Anda hanya pernah melihat kunci publik dan challenge yang telah ditandatangani. WebAuthn adalah standar dasar dari W3C dan FIDO Alliance, dan menjadi komponen inti dari FIDO2.

- Tidak ada rahasia bersama yang dikirim atau disimpan, sehingga tidak ada padanan hash kata sandi yang bisa dibobol penyerang secara offline
- Tanda tangan terikat ketat pada origin yang memintanya, sehingga halaman phishing klasik yang meniru halaman login Anda tidak bisa memperoleh kredensial yang bisa dipakai
- Mekanisme sinkronisasi dari Apple, Google, dan Microsoft memungkinkan passkey yang dibuat di satu perangkat langsung berfungsi di perangkat lain milik pengguna tanpa langkah pendaftaran tambahan

> Jika aplikasi Anda sudah memiliki alur kata sandi atau magic link, perlakukan passkey sebagai opsi tambahan terlebih dahulu. Biarkan pengguna menambahkan passkey dari halaman pengaturan akun, lalu jadikan itu metode default yang disarankan saat login setelah adopsi meningkat, alih-alih memaksa migrasi total sejak hari pertama.

## Proses registrasi (registration ceremony)

Mendaftarkan passkey adalah pertukaran empat langkah antara server dan browser Anda. Server Anda tidak pernah membuat pasangan kunci sendiri; server hanya menerbitkan sekumpulan opsi yang ditandatangani lalu memverifikasi hasil yang dibuat oleh autentikator.

- Klien meminta opsi registrasi ke server Anda, dan server harus sudah dalam keadaan terautentikasi karena proses ini menautkan kredensial baru ke akun yang sudah dikenal
- Server memanggil fungsi bantu seperti generateRegistrationOptions untuk membuat challenge acak beserta informasi relying party dan pengguna, lalu menyimpan challenge tersebut di tempat yang bisa diperiksa kembali nanti
- Browser memanggil metode create dari WebAuthn dengan opsi tersebut, memicu prompt biometrik atau PIN dari platform, dan mengembalikan objek attestation yang sudah ditandatangani
- Server memverifikasi attestation tersebut terhadap challenge yang tersimpan dan origin yang diharapkan, lalu menyimpan kunci publik, ID kredensial, dan transport yang didukung ke data pengguna

Daftar excludeCredentials lebih penting dari yang terlihat: mengirim ID kredensial pengguna yang sudah ada ke generateRegistrationOptions mencegah autentikator yang sama diam-diam mendaftarkan passkey duplikat, yang jika dibiarkan akan membingungkan pengguna karena muncul dua entri hampir identik di pengelola kata sandi mereka. Ada baiknya juga mencatat setiap percobaan verifikasi yang gagal selama masa rollout, karena lonjakan biasanya menandakan masalah selisih waktu (clock skew) antara server Anda dan jendela kedaluwarsa challenge, bukan serangan sungguhan.

## Menghubungkan alur registrasi di sisi klien

Di sisi klien, paket browser dari SimpleWebAuthn menyediakan fungsi bantu startRegistration yang membungkus pemanggilan create WebAuthn mentah, mengonversi field base64url yang dikirim server menjadi ArrayBuffer yang diharapkan API browser, dan menyeragamkan penanganan error di berbagai browser. Pola di bawah ini mengambil opsi, memanggil API browser, lalu mengirim hasilnya kembali untuk diverifikasi.

> WebAuthn membutuhkan secure context. Fitur ini berjalan di localhost untuk pengembangan, tetapi di produksi relying party ID harus cocok dengan domain yang dilayani lewat HTTPS, dan tidak bisa lintas domain yang tidak berkaitan. Jika staging Anda berjalan di top-level domain berbeda dari produksi, Anda perlu mendaftarkan kredensial terpisah per relying party ID; passkey tidak berpindah otomatis antar domain.

## Apa yang perlu disimpan, dan dari mana keamanannya sebenarnya berasal

Database Anda pada akhirnya hanya menyimpan sedikit data: ID kredensial, kunci publik, penghitung tanda tangan, dan transport yang dilaporkan autentikator. Semua itu tidak sensitif seperti halnya hash kata sandi, karena tidak satu pun dapat digunakan ulang untuk mengautentikasi sebagai pengguna tanpa kunci privat yang bersesuaian. Artinya, kebocoran database yang membocorkan seluruh tabel kredensial Anda pun tidak serta-merta memberi penyerang cara untuk login sebagai siapa pun.

| Data | Lokasi penyimpanan | Mengapa penting |
| --- | --- | --- |
| Kunci publik + ID kredensial | Database Anda, terkait dengan pengguna | Dipakai untuk memverifikasi tanda tangan berikutnya; tidak berguna bagi penyerang tanpa kunci privat |
| Challenge | Sesi server atau cache berumur pendek | Mencegah pemutaran ulang respons registrasi atau login lama |
| Kunci privat | Tidak pernah meninggalkan autentikator | Alasan utama passkey tahan terhadap kebocoran database dan phishing |

## Proses login (authentication ceremony)

Autentikasi mirip dengan registrasi tetapi lebih sederhana karena tidak ada attestation yang perlu diurai, hanya assertion yang sudah ditandatangani. Server membuat opsi autentikasi, browser memanggil metode get dari WebAuthn, dan autentikator menandatangani challenge dengan kunci privat yang sudah dimilikinya.

- Mengatur userVerification ke preferred atau required memaksa pemeriksaan biometrik atau PIN, bukan sekadar deteksi kehadiran, dan ini penting untuk hal yang sensitif secara keamanan
- Mengirim allowCredentials dengan ID kredensial yang sudah dikenal mempercepat tampilan saat Anda sudah tahu siapa yang login, tetapi mengosongkannya memungkinkan login discoverable tanpa username di platform yang mendukungnya
- Setelah verifyAuthenticationResponse berhasil, perbarui penghitung tanda tangan yang tersimpan; penghitung yang mundur adalah sinyal bahwa kredensial mungkin telah diduplikasi dan layak ditandai

Kredensial discoverable (resident key) adalah yang memungkinkan pengguna cukup menekan "masuk dengan passkey" tanpa mengetik username terlebih dahulu. Mengatur residentKey ke required saat registrasi adalah yang membuat pengalaman ini mungkin, dan ini adalah bentuk terdekat WebAuthn dengan login tanpa kata sandi dan tanpa username yang sesungguhnya.

## Opsi cadangan bagi pengguna tanpa passkey

Belum semua perangkat mendukung passkey dengan baik, dan sebagian pengguna berada di perangkat bersama atau terkunci kebijakan di mana autentikator platform dinonaktifkan. Jangan jadikan passkey satu-satunya pintu masuk akun.

- Pertahankan alur kata sandi atau magic link lewat email sebagai metode kedua, alih-alih menghapusnya begitu passkey diluncurkan
- Sediakan cara mendaftarkan security key portabel seperti YubiKey bagi pengguna yang autentikator platformnya tidak tersedia atau dinonaktifkan kebijakan IT
- Lakukan deteksi fitur dengan PublicKeyCredential sebelum menampilkan antarmuka passkey sama sekali, sehingga browser yang tidak mendukung langsung melihat metode cadangan alih-alih tombol yang rusak

Passkey adalah salah satu peningkatan keamanan langka yang juga memperbaiki pengalaman pengguna, asalkan proses registrasi dan login disambungkan dengan benar. Bagian berat sudah distandarkan oleh WebAuthn sendiri; library seperti SimpleWebAuthn hanya membebaskan Anda dari menulis ulang decoding CBOR dan verifikasi attestation secara manual. Mulailah dengan menawarkan passkey sebagai opsi tambahan, pastikan jalur penyimpanan dan cadangan sudah benar, pantau sinyal penghitung tanda tangan dan kegagalan verifikasi selama rollout, dan kolom kata sandi bisa perlahan menjadi opsional alih-alih wajib.

## Sumber

- MDN — Web Authentication API
- Wikipedia — WebAuthn
- MDN — Metode CredentialsContainer: create()

## Source links

- https://developer.mozilla.org/en-US/docs/Web/API/Web_Authentication_API
- https://en.wikipedia.org/wiki/WebAuthn
- https://developer.mozilla.org/en-US/docs/Web/API/CredentialsContainer/create

## FAQ

### Apa itu passkey dan apa bedanya dengan kata sandi?

Passkey adalah kredensial WebAuthn yang dibangun di atas pasangan kunci asimetris, bukan rahasia bersama. Kunci privat tidak pernah meninggalkan autentikator perangkat pengguna, sehingga berbeda dari kata sandi, tidak ada apa pun yang bisa dipakai ulang oleh penyerang jika terjadi kebocoran server atau halaman phishing.

### Apakah saya perlu library khusus untuk menambahkan passkey ke aplikasi Next.js?

Anda bisa memanggil API browser WebAuthn secara langsung, tetapi kebanyakan tim memakai library bantu seperti SimpleWebAuthn untuk membuat opsi registrasi dan autentikasi serta memverifikasi responsnya, karena library ini menangani parsing CBOR dan verifikasi attestation untuk Anda. Route handler di Next.js App Router adalah tempat yang wajar untuk menaruh endpoint opsi dan verifikasi di sisi server.

### Apakah pengguna masih bisa login jika perangkatnya tidak mendukung passkey?

Ya, dan penting untuk selalu menyediakan opsi cadangan. Lakukan deteksi fitur PublicKeyCredential sebelum menampilkan antarmuka passkey, dan pertahankan alur kata sandi atau magic link di samping passkey agar pengguna dengan browser yang tidak mendukung atau perangkat terkunci kebijakan tidak pernah terblokir.

### Mengapa WebAuthn mewajibkan HTTPS di produksi?

WebAuthn mengikat setiap kredensial ke secure context dan relying party ID tertentu yang terkait domain Anda, yang mencegah penyerang di jaringan tidak aman mencegat atau memutar ulang proses tersebut. Fitur ini berjalan di localhost untuk pengembangan lokal, tetapi trafik produksi harus dilayani lewat HTTPS dengan relying party ID yang sesuai.

### Apa sebenarnya yang tersimpan di database saat pengguna mendaftarkan passkey?

Yang tersimpan hanya kunci publik, ID kredensial, penghitung tanda tangan, dan transport yang dilaporkan autentikator. Semua data ini tidak sensitif seperti hash kata sandi, karena tidak bisa dipakai untuk mengautentikasi sebagai pengguna tanpa kunci privat yang bersesuaian, yang tetap berada di perangkat pengguna.

---

- HTML version: https://www.matthewswong.com/id/blog/passkeys-webauthn-nextjs-authentication/
- English: https://www.matthewswong.com/en/blog/passkeys-webauthn-nextjs-authentication/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
