1. Pengantar
Apa itu FIDO2 API?
FIDO2 API memungkinkan aplikasi Android membuat dan menggunakan kredensial berbasis kunci publik yang kuat dan disahkan untuk tujuan mengautentikasi pengguna. API ini menyediakan implementasi Klien WebAuthn, yang mendukung penggunaan pengautentikasi BLE, NFC, dan roaming USB (kunci keamanan) serta pengautentikasi platform, yang memungkinkan pengguna melakukan autentikasi menggunakan sidik jari atau kunci layar.
Yang akan Anda build...
Dalam codelab ini, Anda akan mem-build aplikasi Android dengan fungsi autentikasi ulang sederhana menggunakan sensor sidik jari. "Autentikasi ulang" adalah saat pengguna login ke aplikasi, lalu melakukan autentikasi ulang saat mereka beralih kembali ke aplikasi Anda, atau saat mencoba mengakses bagian penting aplikasi Anda. Kasus yang terakhir ini juga disebut sebagai "autentikasi langkah-langkah".
Yang akan Anda pelajari ...
Anda akan mempelajari cara memanggil Android FIDO2 API dan opsi yang dapat Anda berikan agar dapat digunakan untuk berbagai situasi. Anda juga akan mempelajari praktik terbaik khusus autentikasi ulang.
Yang akan Anda butuhkan ...
- Perangkat Android dengan sensor sidik jari (meskipun tanpa sensor sidik jari, kunci layar dapat memberikan fungsi verifikasi pengguna yang setara)
- Android OS 7.0 atau yang lebih baru dengan update terbaru. Pastikan untuk mendaftarkan sidik jari (atau kunci layar).
2. Mempersiapkan
Membuat Clone Repositori
Lihat repositori GitHub.
https://github.com/android/codelab-fido2
$ git clone https://github.com/android/codelab-fido2.git
Apa yang akan kita terapkan?
- Mengizinkan pengguna mendaftarkan "pengautentikasi platform yang memverifikasi pengguna" (ponsel Android yang memiliki sensor sidik jari akan bertindak sebagai satu perangkat).
- Mengizinkan pengguna mengautentikasi ulang dirinya ke aplikasi menggunakan sidik jari mereka.
Anda dapat melihat pratinjau apa yang akan dibangun di sini.
Memulai project codelab Anda
Aplikasi yang sudah selesai akan mengirimkan permintaan ke server di https://webauthn-codelab.glitch.me. Anda dapat mencoba versi web dari aplikasi yang sama di sana.
Anda akan mengerjakan versi aplikasi Anda sendiri.
- Buka halaman edit situs di https://glitch.com/edit/#!/webauthn-codelab.
- Cari "Remix untuk Mengedit" di pojok kanan atas. Dengan menekan tombol, Anda dapat "{i>fork<i}" kode dan melanjutkan dengan versi Anda sendiri bersama dengan URL proyek baru.
- Salin nama project di kiri atas (Anda dapat mengubahnya sesuai keinginan).
- Tempel ke bagian
HOSTNAME
file.env
di glitch.
3. Mengaitkan aplikasi dan situs Anda dengan Digital Asset Links
Untuk menggunakan FIDO2 API di aplikasi Android, kaitkan dengan situs dan bagikan kredensial di antara keduanya. Untuk melakukannya, manfaatkan Digital Asset Links. Anda dapat mendeklarasikan pengaitan dengan menghosting file JSON Digital Asset Links di situs, dan menambahkan link ke file Digital Asset Link ke manifes aplikasi.
Menghosting .well-known/assetlinks.json
di domain Anda
Anda dapat menentukan pengaitan antara aplikasi dan situs dengan membuat file JSON dan meletakkannya di .well-known/assetlinks.json
. Untungnya, kita memiliki kode server yang menampilkan file assetlinks.json
secara otomatis, hanya dengan menambahkan parameter lingkungan berikut ke file .env
yang mengalami glitch:
ANDROID_PACKAGENAME
: Nama paket aplikasi Anda (com.example.android.fido2)ANDROID_SHA256HASH
: SHA256 Hash sertifikat penandatanganan Anda
Untuk mendapatkan hash SHA256 sertifikat penandatanganan developer, gunakan perintah di bawah. Sandi default keystore debug adalah "android".
$ keytool -exportcert -list -v -alias androiddebugkey -keystore ~/.android/debug.keystore
Dengan mengakses https://<your-project-name>.glitch.me/.well-known/assetlinks.json
, Anda akan melihat string JSON seperti ini:
[{
"relation": ["delegate_permission/common.handle_all_urls", "delegate_permission/common.get_login_creds"],
"target": {
"namespace": "web",
"site": "https://<your-project-name>.glitch.me"
}
}, {
"relation": ["delegate_permission/common.handle_all_urls", "delegate_permission/common.get_login_creds"],
"target": {
"namespace": "android_app",
"package_name": "com.example.android.fido2",
"sha256_cert_fingerprints": ["DE:AD:BE:EF:..."]
}
}]
Membuka project di Android Studio
Klik "Open an existing Android Studio project" di layar sambutan Android Studio.
Pilih "android" folder di dalam repositori check out.
Mengaitkan aplikasi dengan remix Anda
Buka file gradle.properties
. Di bagian bawah file, ubah URL host ke remix Glitch yang baru saja Anda buat.
// ...
# The URL of the server
host=https://<your-project-name>.glitch.me
Pada tahap ini, konfigurasi Digital Asset Links Anda seharusnya sudah siap.
4. Lihat cara kerja aplikasi sekarang
Mari kita mulai dengan memeriksa cara kerja aplikasi tersebut sekarang. Pastikan untuk memilih "app-start" di combobox konfigurasi run. Klik "Jalankan" (segitiga hijau di samping combobox) untuk meluncurkan aplikasi di perangkat Android yang terhubung.
Saat meluncurkan aplikasi, Anda akan melihat layar untuk mengetik nama pengguna. Ini adalah UsernameFragment
. Untuk tujuan demonstrasi, aplikasi dan server menerima nama pengguna apa pun. Cukup ketik sesuatu, lalu tekan "Next".
Layar berikutnya yang Anda lihat adalah AuthFragment
. Di sini, pengguna dapat login dengan sandi. Nantinya, kita akan menambahkan fitur login dengan FIDO2 di sini. Sekali lagi, untuk tujuan demonstrasi, aplikasi dan server akan menerima sandi apa pun. Cukup ketik sesuatu dan tekan "Login".
Ini adalah layar terakhir aplikasi ini, HomeFragment
. Untuk saat ini, Anda hanya melihat daftar kredensial kosong di sini. Menekan "Reauth" membawa Anda kembali ke AuthFragment
. Menekan "Logout" membawa Anda kembali ke UsernameFragment
. Tombol tindakan mengambang dengan "+" tidak melakukan apa-apa sekarang, tapi
itu akan memulai pendaftaran sebuah
kredensial baru setelah Anda menerapkan alur pendaftaran FIDO2.
Sebelum mulai membuat kode, berikut teknik yang berguna. Di Android Studio, tekan "TODO" di bagian bawah. Tindakan ini akan menampilkan daftar semua TODO dalam codelab ini. Kita akan mulai dengan TODO pertama di bagian berikutnya.
5. Mendaftarkan kredensial menggunakan sidik jari
Untuk mengaktifkan autentikasi menggunakan sidik jari, pertama-tama Anda harus mendaftarkan kredensial yang dibuat oleh pengguna yang memverifikasi pengautentikasi platform - pengautentikasi yang disematkan di perangkat dan memverifikasi pengguna menggunakan biometrik, seperti sensor sidik jari.
Seperti yang telah kita lihat di bagian sebelumnya, tombol aksi mengambang sekarang tidak melakukan apa-apa. Mari kita lihat cara mendaftarkan kredensial baru.
Memanggil API server: /auth/registerRequest
Buka AuthRepository.kt
dan temukan TODO(1).
Di sini, registerRequest
adalah metode yang dipanggil saat FAB ditekan. Kita akan membuat metode ini memanggil API server /auth/registerRequest
. API menampilkan ApiResult
dengan semua PublicKeyCredentialCreationOptions
yang diperlukan klien untuk membuat kredensial baru.
Selanjutnya, kita dapat memanggil getRegisterPendingIntent
dengan opsi yang ada. FIDO2 API ini menampilkan PendingIntent Android untuk membuka dialog sidik jari dan menghasilkan kredensial baru, dan kita dapat mengembalikan PendingIntent tersebut ke pemanggil.
Metode akan terlihat seperti di bawah ini.
suspend fun registerRequest(): PendingIntent? {
fido2ApiClient?.let { client ->
try {
val sessionId = dataStore.read(SESSION_ID)!!
when (val apiResult = api.registerRequest(sessionId)) {
ApiResult.SignedOutFromServer -> forceSignOut()
is ApiResult.Success -> {
if (apiResult.sessionId != null) {
dataStore.edit { prefs ->
prefs[SESSION_ID] = apiResult.sessionId
}
}
val task = client.getRegisterPendingIntent(apiResult.data)
return task.await()
}
}
} catch (e: Exception) {
Log.e(TAG, "Cannot call registerRequest", e)
}
}
return null
}
Membuka dialog sidik jari untuk pendaftaran
Buka HomeFragment.kt
dan temukan TODO(2).
Di sinilah UI mendapatkan Intent kembali dari AuthRepository
. Di sini, kita akan menggunakan anggota createCredentialIntentLauncher
untuk meluncurkan PendingIntent yang kita dapatkan sebagai hasil dari langkah sebelumnya. Tindakan ini akan membuka dialog untuk pembuatan kredensial.
binding.add.setOnClickListener {
lifecycleScope.launch {
val intent = viewModel.registerRequest()
if (intent != null) {
createCredentialIntentLauncher.launch(
IntentSenderRequest.Builder(intent).build()
)
}
}
}
Menerima ActivityResult dengan Kredensial baru
Buka HomeFragment.kt
dan temukan TODO(3).
Metode handleCreateCredentialResult
ini dipanggil setelah dialog sidik jari ditutup. Jika kredensial berhasil dibuat, anggota data
dari ActivityResult akan berisi informasi kredensial.
Pertama, kita harus mengekstrak PublicKeyCredential dari data
. Intent data memiliki kolom tambahan array byte dengan kunci Fido.FIDO2_KEY_CREDENTIAL_EXTRA
. Anda dapat menggunakan metode statis di PublicKeyCredential
yang disebut deserializeFromBytes
untuk mengubah array byte menjadi objek PublicKeyCredential
.
Selanjutnya, periksa apakah anggota response
objek kredensial ini adalah AuthenticationErrorResponse
. Jika ya, maka terjadi error saat membuat kredensial; jika tidak, kita bisa mengirim
kredensial ke backend.
Metode yang sudah selesai akan terlihat seperti ini:
private fun handleCreateCredentialResult(activityResult: ActivityResult) {
val bytes = activityResult.data?.getByteArrayExtra(Fido.FIDO2_KEY_CREDENTIAL_EXTRA)
when {
activityResult.resultCode != Activity.RESULT_OK ->
Toast.makeText(requireContext(), R.string.cancelled, Toast.LENGTH_LONG).show()
bytes == null ->
Toast.makeText(requireContext(), R.string.credential_error, Toast.LENGTH_LONG)
.show()
else -> {
val credential = PublicKeyCredential.deserializeFromBytes(bytes)
val response = credential.response
if (response is AuthenticatorErrorResponse) {
Toast.makeText(requireContext(), response.errorMessage, Toast.LENGTH_LONG)
.show()
} else {
viewModel.registerResponse(credential)
}
}
}
}
Memanggil API server: /auth/registerResponse
Buka AuthRepository.kt
dan temukan TODO(4).
Metode registerResponse
ini dipanggil setelah UI berhasil membuat kredensial baru, dan kita ingin mengirimkannya kembali ke server.
Objek PublicKeyCredential
memiliki informasi tentang kredensial yang baru dibuat di dalamnya. Kita sekarang ingin mengingat ID kunci lokal sehingga kita dapat membedakannya dari kunci lain yang terdaftar di server. Di objek PublicKeyCredential
, ambil properti rawId
dan masukkan dalam variabel string lokal menggunakan toBase64
.
Sekarang kita siap
untuk mengirim informasi ke server. Gunakan api.registerResponse
untuk memanggil API server dan mengirim kembali respons. Nilai yang ditampilkan berisi daftar semua kredensial yang terdaftar di server, termasuk yang baru.
Terakhir, kita dapat menyimpan hasilnya di DataStore
. Daftar kredensial harus disimpan dengan kunci CREDENTIALS
sebagai StringSet
. Anda dapat menggunakan toStringSet
untuk mengonversi daftar kredensial menjadi StringSet
.
Selain itu, kita menyimpan ID kredensial dengan kunci LOCAL_CREDENTIAL_ID
.
suspend fun registerResponse(credential: PublicKeyCredential) {
try {
val sessionId = dataStore.read(SESSION_ID)!!
val credentialId = credential.rawId.toBase64()
when (val result = api.registerResponse(sessionId, credential)) {
ApiResult.SignedOutFromServer -> forceSignOut()
is ApiResult.Success -> {
dataStore.edit { prefs ->
result.sessionId?.let { prefs[SESSION_ID] = it }
prefs[CREDENTIALS] = result.data.toStringSet()
prefs[LOCAL_CREDENTIAL_ID] = credentialId
}
}
}
} catch (e: ApiException) {
Log.e(TAG, "Cannot call registerResponse", e)
}
}
Jalankan aplikasi, dan Anda akan dapat mengklik FAB dan mendaftarkan kredensial baru.
6. Mengautentikasi pengguna dengan sidik jari
Kita kini memiliki kredensial yang terdaftar di aplikasi dan server. Sekarang kita dapat menggunakannya untuk mengizinkan pengguna login. Kami menambahkan fitur login dengan sidik jari ke AuthFragment
. Saat pengguna membukanya, dialog sidik jari akan ditampilkan. Saat autentikasi berhasil, pengguna akan dialihkan ke HomeFragment
.
Memanggil API server: /auth/signinRequest
Buka AuthRepository.kt
dan temukan TODO(5).
Metode signinRequest
ini dipanggil saat AuthFragment
dibuka. Di sini, kita ingin meminta server dan melihat apakah kita dapat mengizinkan pengguna login dengan FIDO2.
Pertama, kita harus mengambil PublicKeyCredentialRequestOptions
dari server. Gunakan api.signInRequest
untuk memanggil API server. ApiResult
yang ditampilkan berisi PublicKeyCredentialRequestOptions
.
Dengan PublicKeyCredentialRequestOptions
, kita dapat menggunakan getSignIntent
FIDO2 API untuk membuat PendingIntent guna membuka dialog sidik jari.
Terakhir, kita dapat mengembalikan PendingIntent kembali ke UI.
suspend fun signinRequest(): PendingIntent? {
fido2ApiClient?.let { client ->
val sessionId = dataStore.read(SESSION_ID)!!
val credentialId = dataStore.read(LOCAL_CREDENTIAL_ID)
if (credentialId != null) {
when (val apiResult = api.signinRequest(sessionId, credentialId)) {
ApiResult.SignedOutFromServer -> forceSignOut()
is ApiResult.Success -> {
val task = client.getSignPendingIntent(apiResult.data)
return task.await()
}
}
}
}
return null
}
Membuka dialog sidik jari untuk pernyataan
Buka AuthFragment.kt
dan temukan TODO(6).
Ini hampir sama dengan apa yang kita lakukan untuk pendaftaran. Kita dapat meluncurkan dialog sidik jari dengan anggota signIntentLauncher
.
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
launch {
viewModel.signinRequests.collect { intent ->
signIntentLauncher.launch(
IntentSenderRequest.Builder(intent).build()
)
}
}
launch {
...
}
}
Menangani ActivityResult
Buka AuthFragment.kt dan cari TODO(7).
Sekali lagi, ini sama dengan apa yang kami lakukan untuk pendaftaran. Kita dapat mengekstrak PublicKeyCredential
, memeriksa error, dan meneruskannya ke ViewModel.
private fun handleSignResult(activityResult: ActivityResult) {
val bytes = activityResult.data?.getByteArrayExtra(Fido.FIDO2_KEY_CREDENTIAL_EXTRA)
when {
activityResult.resultCode != Activity.RESULT_OK ->
Toast.makeText(requireContext(), R.string.cancelled, Toast.LENGTH_SHORT).show()
bytes == null ->
Toast.makeText(requireContext(), R.string.auth_error, Toast.LENGTH_SHORT)
.show()
else -> {
val credential = PublicKeyCredential.deserializeFromBytes(bytes)
val response = credential.response
if (response is AuthenticatorErrorResponse) {
Toast.makeText(requireContext(), response.errorMessage, Toast.LENGTH_SHORT)
.show()
} else {
viewModel.signinResponse(credential)
}
}
}
}
Memanggil API server: /auth/signinResponse
Buka AuthRepository.kt
dan temukan TODO(8).
Objek PublicKeyCredential memiliki ID kredensial di dalamnya sebagai keyHandle
. Sama seperti yang kita lakukan dalam alur pendaftaran, mari kita simpan ini dalam variabel string lokal sehingga kita dapat menyimpannya nanti.
Sekarang kita siap memanggil API server dengan api.signinResponse
. Nilai yang ditampilkan berisi daftar kredensial.
Pada tahap ini, proses login berhasil. Kita harus menyimpan semua hasil di DataStore
. Daftar kredensial harus disimpan sebagai StringSet dengan kunci CREDENTIALS
. ID kredensial lokal yang kita simpan di atas harus disimpan sebagai string dengan kunci LOCAL_CREDENTIAL_ID
.
Terakhir, kita perlu memperbarui status login sehingga UI dapat mengalihkan pengguna ke HomeFragment. Hal ini dapat dilakukan dengan memunculkan objek SignInState.SignedIn
ke SharedFlow bernama signInStateMutable
. Kita juga ingin memanggil refreshCredentials
untuk mengambil kredensial pengguna sehingga pengguna tersebut akan tercantum di UI.
suspend fun signinResponse(credential: PublicKeyCredential) {
try {
val username = dataStore.read(USERNAME)!!
val sessionId = dataStore.read(SESSION_ID)!!
val credentialId = credential.rawId.toBase64()
when (val result = api.signinResponse(sessionId, credential)) {
ApiResult.SignedOutFromServer -> forceSignOut()
is ApiResult.Success -> {
dataStore.edit { prefs ->
result.sessionId?.let { prefs[SESSION_ID] = it }
prefs[CREDENTIALS] = result.data.toStringSet()
prefs[LOCAL_CREDENTIAL_ID] = credentialId
}
signInStateMutable.emit(SignInState.SignedIn(username))
refreshCredentials()
}
}
} catch (e: ApiException) {
Log.e(TAG, "Cannot call registerResponse", e)
}
}
Jalankan aplikasi dan klik "Reauth" untuk membuka AuthFragment
. Sekarang Anda akan melihat dialog sidik jari yang meminta Anda untuk login dengan sidik jari.
Selamat! Anda kini telah mempelajari cara menggunakan FIDO2 API di Android untuk pendaftaran dan login.
7. Selamat!
Anda berhasil menyelesaikan codelab - Android FIDO2 API pertama Anda.
Yang telah Anda pelajari
- Cara mendaftarkan kredensial menggunakan pengautentikasi platform yang memverifikasi pengguna.
- Cara mengautentikasi pengguna menggunakan pengautentikasi terdaftar.
- Opsi yang tersedia untuk mendaftarkan pengautentikasi baru.
- Praktik terbaik UX untuk autentikasi ulang menggunakan sensor biometrik.
Langkah berikutnya
- Pelajari cara membangun pengalaman serupa di situs.
Anda dapat mempelajarinya dengan mencoba codelab WebAuthn pertama Anda.
Resource
Terima kasih banyak kepada Yuriy Ackermann dari FIDO Alliance atas bantuan Anda.