İlk Android FIDO2 API'niz

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.

c2234c42ba8a6ef1.png

Uygulamanın kendi sürümünüz üzerinde çalışacaksınız.

  1. https://glitch.com/edit/#!/webauthn-codelab adresindeki web sitesinin düzenleme sayfasına gidin.
  2. "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. 9ef108869885e4ce.png.
  3. Sol üstteki proje adını kopyalayın (istediğiniz gibi değiştirebilirsiniz). c91d0d59c61021a4.png.
  4. Kodu .env dosyasının HOSTNAME bölümüne geçici olarak yapıştırın. 889b55b1cf74b894.png

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.

1062875cf11ffb95.png

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.

29351fb97062b43c.png

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.

bd9007614a9a3644.png

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.

d9caba817a0a99bd.png

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)

1cfcc6c884020e37.png

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.

e5a811bbc7cd7b30.png

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.

37ce78fdf2759832.png

Ö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.

7d64d9289c5a3cbc.png

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.

45f81419f84952c8.png

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.