1. Giriş
FIDO2 API nedir?
FIDO2 API'si, Android uygulamalarının kullanıcıların kimliğini doğrulamak amacıyla güçlü, onaylanmış ortak anahtar tabanlı kimlik bilgileri oluşturmasına ve kullanmasına olanak tanır. API; BDE, NFC ve USB dolaşım kimlik doğrulayıcılarının (güvenlik anahtarları) kullanımını destekleyen bir WebAuthn İstemcisi uygulaması sağlar. Ayrıca, kullanıcının parmak izlerini veya ekran kilidini kullanarak kimlik doğrulaması yapmasını sağlayan bir platform kimlik doğrulayıcısı da kullanılabilir.
Neler oluşturacaksınız?
Bu codelab'de, parmak izi sensörünü kullanarak basit bir yeniden kimlik doğrulama işlevine sahip Android uygulaması geliştireceksiniz. "Yeniden kimlik doğrulama" Kullanıcının bir uygulamada oturum açtıktan sonra uygulamanıza geri döndüğünde veya uygulamanızın önemli bir bölümüne erişmeye çalıştığında yeniden kimlik doğrulaması yapması anlamına gelir. "Adım adım kimlik doğrulaması" da denir.
Öğrenecekleriniz...
Android FIDO2 API'yi nasıl çağıracağınızı ve çeşitli özel günlere yönelik yararlanabileceğiniz seçenekleri öğreneceksiniz. Ayrıca, yeniden kimlik doğrulama ile ilgili en iyi uygulamaları öğreneceksiniz.
Gerekenler...
- Parmak izi sensörlü Android cihaz (parmak izi sensörü olmasa bile ekran kilidi eşdeğer bir kullanıcı doğrulama işlevi sağlayabilir)
- En son güncellemelere sahip Android OS 7.0 veya sonraki sürümler. Parmak izi (veya ekran kilidi) kaydettiğinizden emin olun.
2. Kurulum
Depoyu klonlama
GitHub deposuna göz atın.
https://github.com/android/codelab-fido2
$ git clone https://github.com/android/codelab-fido2.git
Neyi uygulayacağız?
- Kullanıcıların "kullanıcı doğrulama platformu kimlik doğrulayıcısı" kaydettirmesine izin verme (parmak izi sensörünün kendisi olan Android telefon, parmak izi sensörü olarak çalışır).
- Kullanıcıların, parmak izlerini kullanarak uygulamada kendi kimliklerini yeniden doğrulamalarına izin verin.
Neler geliştireceğinizi buradan önizleyebilirsiniz.
Codelab projenizi başlatma
Tamamlanan uygulama, istekleri https://webauthn-codelab.glitch.me adresindeki bir sunucuya gönderir. Aynı uygulamanın web sürümünü orada deneyebilirsiniz.
Uygulamanın kendi sürümünüz üzerinde çalışacaksınız.
- https://glitch.com/edit/#!/webauthn-codelab adresindeki web sitesinin düzenleme sayfasına gidin.
- "Düzenlemek için remiks"i bulun düğmesini tıklayın. Düğmeye basarak "çatallama" işlemini bu kodu yazıp kendi sürümünüzle ve yeni proje URL'siyle devam edin.
.
- Sol üstteki proje adını kopyalayın (istediğiniz gibi değiştirebilirsiniz).
.
- Kodu
.env
dosyasınınHOSTNAME
bölümüne geçici olarak yapıştırın.
3. Uygulamanızı ve bir web sitesini Digital Asset Links ile ilişkilendirme
Bir Android uygulamasında FIDO2 API'yi kullanmak için API'yi bir web sitesiyle ilişkilendirip kimlik bilgilerini bu uygulamalar arasında paylaşın. Bunu yapmak için Dijital Varlık Bağlantıları'ndan yararlanın. Web sitenizde bir Digital Asset Links JSON dosyası barındırıp uygulamanızın manifest dosyasına Digital Asset Link dosyasının bir bağlantısını ekleyerek ilişkilendirmeleri bildirebilirsiniz.
.well-known/assetlinks.json
hizmetini kendi alanınızda barındırın
Bir JSON dosyası oluşturup bunu .well-known/assetlinks.json
konumuna yerleştirerek uygulamanızla web sitesi arasında bir ilişkilendirme tanımlayabilirsiniz. Neyse ki, aşağıdaki ortam parametrelerini hatadaki .env
dosyasına ekleyerek assetlinks.json
dosyasını otomatik olarak görüntüleyen bir sunucu kodumuz var:
ANDROID_PACKAGENAME
: Uygulamanızın paket adı (com.example.android.fido2)ANDROID_SHA256HASH
: İmza sertifikanızın SHA256 karması
Geliştirici imzalama sertifikanızın SHA256 karmasını almak için aşağıdaki komutu kullanın. Hata ayıklama anahtar deposunun varsayılan şifresi "android"dir.
$ keytool -exportcert -list -v -alias androiddebugkey -keystore ~/.android/debug.keystore
https://<your-project-name>.glitch.me/.well-known/assetlinks.json
öğesine eriştiğinizde şuna benzer bir JSON dizesi görürsünüz:
[{
"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:..."]
}
}]
Projeyi Android Studio'da açma
"Mevcut bir Android Studio projesini aç"ı tıklayın "Android Studio'nun karşılama ekranında" tıkladık.
"Android"i seçin klasörünü kontrol edin.
Uygulamayı remiksinizle ilişkilendirin
gradle.properties
dosyasını aç. Dosyanın alt kısmında, ana makine URL'sini yeni oluşturduğunuz Glitch remiksi olarak değiştirin.
// ...
# The URL of the server
host=https://<your-project-name>.glitch.me
Bu noktada Digital Asset Links yapılandırmanızın ayarlanmış olması gerekir.
4. Uygulamanın şu anda nasıl çalıştığını görün
Uygulamanın şu anda nasıl çalıştığına göz atarak başlayalım. Yapılandırmayı çalıştırma açılan kutusunda "app-start"ı seçtiğinizden emin olun. "Çalıştır"ı tıklayın. (gelen kutunun yanındaki yeşil üçgen) simgesini tıklayın.
Uygulamayı başlattığınızda kullanıcı adınızı yazmanız için bir ekran görürsünüz. Bu, UsernameFragment
radyosudur. Uygulama ve sunucu, gösterim amacıyla herhangi bir kullanıcı adını kabul etmelidir. Bir şey yazıp "İleri"ye basmanız yeterlidir.
Göreceğiniz bir sonraki ekran AuthFragment
. Kullanıcılar bu bölümde şifre ile oturum açabilir. Daha sonra buraya FIDO2 ile oturum açmak için bir özellik ekleyeceğiz. Tekrarlamak gerekirse, uygulama ve sunucu, gösterim amacıyla herhangi bir şifreyi kabul eder. Bir şey yazıp "Oturum Aç"a basmanız yeterlidir.
Bu uygulamanın son ekranı, HomeFragment
. Şimdilik burada yalnızca boş bir kimlik bilgisi listesi görüyorsunuz. "Yeniden kimlik doğrulama"ya basılıyor sizi AuthFragment
adresine geri götürür. "Oturumu Kapat"a basma sizi UsernameFragment
adresine geri götürür. "+" işaretli kayan işlem düğmesi işareti şu anda herhangi bir şey yapamamaktadır, ancak bir
yeni kimlik doğrulaması (FIDO2 kayıt akışını uyguladıktan sonra)
Kodlamaya başlamadan önce size şu yararlı tekniklerden birini sunuyoruz: Android Studio'da "YAPILACAKLAR"a basın. dokunun. Bu codelab'deki YAPILACAKLAR'ın tümünün listesi gösterilir. Bir sonraki bölümde ilk YAPILACAKLAR ile başlayalım.
5. Parmak izi kullanarak kimlik bilgisi kaydetme
Parmak izi kullanarak kimlik doğrulamayı etkinleştirmek için ilk olarak, parmak izi sensörü gibi biyometri kullanarak kullanıcıyı doğrulayan, cihaza yerleştirilmiş bir kimlik doğrulayıcı olan kullanıcı doğrulama platformu kimlik doğrulayıcısı tarafından oluşturulan kimlik bilgilerini kaydetmeniz gerekir.
Önceki bölümde gördüğümüz gibi, kayan işlem düğmesi şu anda hiçbir şey yapmıyor. Yeni bir yeterlilik belgesini nasıl kaydedebileceğimize bakalım.
Sunucu API'sini çağırın: /auth/registerRequest
AuthRepository.kt
uygulamasını açın ve TODO(1) işlemini bulun.
Burada registerRequest
, FAB'a basıldığında çağrılan yöntemdir. Bu yöntemin, /auth/registerRequest
sunucu API'sine çağrı yapmasını istiyoruz. API, istemcinin yeni kimlik bilgisi oluşturmak için ihtiyaç duyduğu tüm PublicKeyCredentialCreationOptions
içeren bir ApiResult
döndürür.
Böylece seçenekleri sunmak için getRegisterPendingIntent
ile görüşebiliriz. Bu FIDO2 API, dijital parmak izi iletişim kutusu açıp yeni kimlik bilgisi oluşturmak için bir Android PendingIntent döndürüyor. Bu durumda PendingIntent'i çağrıya döndürebiliriz.
Yöntem aşağıdaki gibi görünür.
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
}
Kayıt için parmak izi iletişim kutusunu aç
HomeFragment.kt
uygulamasını açın ve TODO(2) işlemini bulun.
Kullanıcı arayüzü, AuthRepository
'dan Intent'i burada geri alır. Burada, önceki adımın sonucunda aldığımız PendingIntent'i başlatmak için createCredentialIntentLauncher
üyesini kullanacağız. Bu işlem, kimlik bilgisi oluşturma için bir iletişim kutusu açar.
binding.add.setOnClickListener {
lifecycleScope.launch {
val intent = viewModel.registerRequest()
if (intent != null) {
createCredentialIntentLauncher.launch(
IntentSenderRequest.Builder(intent).build()
)
}
}
}
Yeni Kimlik Bilgisiyle ActivityResult öğesini al
HomeFragment.kt
uygulamasını açın ve TODO(3) işlemini bulun.
Bu handleCreateCredentialResult
yöntemi, parmak izi iletişim kutusu kapatıldıktan sonra çağrılır. Kimlik bilgisi başarıyla oluşturulduysa ActivityResult'un data
üyesi kimlik bilgisi bilgilerini içerir.
İlk olarak data
öğesinden bir PublicKeyCredential çıkarmamız gerekir. Veri amacı, Fido.FIDO2_KEY_CREDENTIAL_EXTRA
anahtarıyla fazladan bir bayt dizisi alanı içeriyor. Bayt dizisini bir PublicKeyCredential
nesnesine dönüştürmek için PublicKeyCredential
içinde deserializeFromBytes
adlı statik bir yöntem kullanabilirsiniz.
Daha sonra, bu kimlik bilgisi nesnesinin response
üyesinin AuthenticationErrorResponse
olup olmadığını kontrol edin. Oluşturuluyorsa kimlik bilgisi oluşturulurken bir hata oluşmuştur; Aksi takdirde, kimlik bilgilerini arka ucumuza gönderebiliriz.
Tamamlanmış yöntem aşağıdaki gibi görünecektir:
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)
}
}
}
}
Sunucu API'sini çağırın: /auth/registerResponse
AuthRepository.kt
uygulamasını açın ve TODO(4) işlemini bulun.
Bu registerResponse
yöntemi, kullanıcı arayüzü yeni bir kimlik bilgisini başarıyla oluşturduktan sonra çağrılır ve sunucuya geri göndermek isteriz.
PublicKeyCredential
nesnesi, içindeki yeni oluşturulan kimlik bilgisi hakkında bilgiler içeriyor. Artık yerel anahtarımızın kimliğini kullanarak sunucuda kayıtlı diğer anahtarlardan ayırt edebilmek istiyoruz. PublicKeyCredential
nesnesinde, rawId
özelliğini alıp toBase64
kullanarak yerel bir dize değişkenine yerleştirin.
Şimdi bilgileri sunucuya göndermeye hazırız. Sunucu API'sini çağırmak ve yanıtı geri göndermek için api.registerResponse
öğesini kullanın. Döndürülen değer, yeni kimlik bilgisi de dahil olmak üzere sunucuda kayıtlı tüm kimlik bilgilerinin listesini içerir.
Son olarak, sonuçları DataStore
aracımıza kaydedebiliriz. Kimlik bilgileri listesi, CREDENTIALS
anahtarıyla StringSet
olarak kaydedilmelidir. Kimlik bilgileri listesini StringSet
öğesine dönüştürmek için toStringSet
öğesini kullanabilirsiniz.
Ayrıca, kimlik bilgisi kimliğini LOCAL_CREDENTIAL_ID
anahtarıyla kaydederiz.
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)
}
}
Uygulamayı çalıştırdığınızda FAB'yi tıklayabilir ve yeni bir kimlik bilgisi kaydedebilirsiniz.
6. Kullanıcının kimliğini parmak iziyle doğrulama
Artık uygulamada ve sunucuda kayıtlı bir kimlik bilgimiz var. Artık bu şifreyi kullanıcının oturum açmasına izin vermek için kullanabiliriz. AuthFragment
uygulamasına parmak iziyle oturum açma özelliği ekliyoruz. Kullanıcı sayfaya geldiğinde bir parmak izi iletişim kutusu gösterilir. Kimlik doğrulama başarılı olduğunda kullanıcı HomeFragment
uygulamasına yönlendirilir.
Sunucu API'sini çağırın: /auth/signinRequest
AuthRepository.kt
uygulamasını açın ve TODO(5) işlemini bulun.
Bu signinRequest
yöntemi, AuthFragment
açıldığında çağrılır. Burada, sunucuyu istemek ve kullanıcının FIDO2 ile oturum açmasına izin verip veremeyeceğimizi görmek istiyoruz.
Öncelikle, PublicKeyCredentialRequestOptions
öğesini sunucudan almamız gerekir. Sunucu API'sini çağırmak için api.signInRequest
yöntemini kullanın. Döndürülen ApiResult
, PublicKeyCredentialRequestOptions
içeriyor.
PublicKeyCredentialRequestOptions
ile parmak izi iletişim kutusunu açmak için PendingIntent oluşturmak amacıyla FIDO2 API getSignIntent
'yi kullanabiliriz.
Son olarak, PendingIntent'i kullanıcı arayüzüne geri döndürebiliriz.
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
}
Onay için parmak izi iletişim kutusunu aç
AuthFragment.kt
uygulamasını açın ve TODO(6) işlemini bulun.
Bu süreç kayıt için yaptıklarımızla hemen hemen aynıdır. signIntentLauncher
üyesiyle parmak izi iletişim kutusunu başlatabiliriz.
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
launch {
viewModel.signinRequests.collect { intent ->
signIntentLauncher.launch(
IntentSenderRequest.Builder(intent).build()
)
}
}
launch {
...
}
}
ActivityResult'u işleme
AuthFragment.kt dosyasını açın ve TODO(7) öğesini bulun.
Bu, kayıt için yaptıklarımızın aynısıdır. PublicKeyCredential
öğesini çıkarıp hata olup olmadığını kontrol edebilir ve ViewModel'e iletebiliriz.
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)
}
}
}
}
Sunucu API'sini çağırın: /auth/signinResponse
AuthRepository.kt
uygulamasını açın ve TODO(8) işlemini bulun.
PublicKeyCredential nesnesinin içinde keyHandle
olarak bir kimlik bilgisi kimliği var. Kayıt akışında yaptığımız gibi, daha sonra kaydedebilmemiz için bunu bir yerel dize değişkenine kaydedelim.
Artık api.signinResponse
ile sunucu API'sini çağırmaya hazırız. Döndürülen değer bir kimlik bilgileri listesi içeriyor.
Bu noktada oturum açma işlemi başarılı olur. Tüm sonuçları DataStore
içinde saklamamız gerekir. Kimlik bilgileri listesi, CREDENTIALS
anahtarıyla StringSet olarak depolanmalıdır. Yukarıda kaydettiğimiz yerel kimlik bilgisi kimliği, LOCAL_CREDENTIAL_ID
anahtarına sahip bir dize olarak depolanmalıdır.
Son olarak, kullanıcı arayüzünün kullanıcıyı HomeFragment'a yönlendirebilmesi için oturum açma durumunu güncellememiz gerekir. Bu işlem, signInStateMutable
adlı SharedFlow'a bir SignInState.SignedIn
nesnesi göndererek yapılabilir. Ayrıca, refreshCredentials
komutunu çağırarak kullanıcının kimlik bilgilerini kullanıcı arayüzünde listelenmelerini sağlamak istiyoruz.
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)
}
}
Uygulamayı çalıştırın ve "Yeniden kimlik doğrulama"yı tıklayın AuthFragment
uygulamasını açın. Şimdi parmak izinizle oturum açmanızı isteyen bir parmak izi iletişim kutusu görmeniz gerekir.
Tebrikler! Artık Android'de kayıt ve oturum açma için FIDO2 API'nin nasıl kullanılacağını öğrendiniz.
7. Tebrikler!
İlk Android FIDO2 API'niz codelab'ini başarıyla tamamladınız.
Öğrendikleriniz
- Kullanıcı doğrulama platformu kimlik doğrulayıcısını kullanarak kimlik bilgisi kaydetme
- Kayıtlı bir kimlik doğrulayıcıyı kullanarak kullanıcı kimliğini doğrulama.
- Yeni bir kimlik doğrulayıcı kaydetmek için kullanılabilecek seçenekler.
- Biyometrik sensör kullanarak yeniden kimlik doğrulama için kullanıcı deneyimiyle ilgili en iyi uygulamalar.
Sonraki adım
- Bir web sitesinde nasıl benzer bir deneyim oluşturacağınızı öğrenin.
İlk WebAuthn codelab'inizi deneyerek öğrenebilirsiniz.
Kaynaklar
Yardımınız için FIDO Alliance'tan Yuriy Ackermann'a özel teşekkürler.