Kimlik Bilgisi Yöneticisi'ni, kimlik bilgisi sağlayıcı çözümünüzle entegre edin

Kimlik Bilgisi Yöneticisi; Android 14'te kullanıma sunulan ve kullanıcı adı-şifre, geçiş anahtarları ve birleşik oturum açma çözümleri (ör. Google ile oturum açma) gibi çoklu oturum açma yöntemlerini destekleyen bir dizi API'yi ifade eder. Credential Manager API'si çağrıldığında, Android sistemi cihazda yüklü tüm kimlik bilgisi sağlayıcılarından kimlik bilgilerini toplar. Bu belgede, bu kimlik bilgisi sağlayıcılara entegrasyon uç noktaları sağlayan API kümesi açıklanmaktadır.

Kurulum

Kimlik bilgisi sağlayıcınıza işlev uygulamadan önce, aşağıdaki bölümlerde gösterilen kurulum adımlarını tamamlayın.

Bağımlılıkları bildirme

Modülünüzün build.gradle dosyasında, Kimlik Bilgisi Yöneticisi kitaplığının en son sürümünü kullanarak bir bağımlılık bildirin:

implementation "androidx.credentials:credentials:1.2.0-{latest}"

Manifest dosyasında hizmet öğesini bildir

Aşağıdaki örnekte gösterildiği gibi, uygulamanızın manifest dosyasında (AndroidManifest.xml) CredentialProviderService sınıfını androidx.credentials kitaplığından genişleten bir hizmet sınıfı için <service> beyanı ekleyin.

<service android:name=".MyCredentialProviderService"
         android:enabled="true"
         android:exported="true"
         android:label="My Credential Provider"
         android:icon="<any drawable icon>"
         android:permission="android.permission.BIND_CREDENTIAL_PROVIDER_SERVICE">
    <intent-filter>
        <action android:name="android.service.credentials.CredentialProviderService"/>
    </intent-filter>
    <meta-data
         android:name="android.credentials.provider"
         android:resource="@xml/provider"/>
</service>

Yukarıda gösterilen izin ve intent filtresi, Kimlik Bilgisi Yöneticisi akışının beklendiği gibi çalışması için vazgeçilmezdir. Bu hizmete yalnızca Android sisteminin bağlanabilmesi için izin gereklidir. Niyet filtresi, Kimlik Yöneticisi tarafından kullanılacak bir kimlik bilgisi sağlayıcısı olarak bu hizmetin keşfedilebilmesi için kullanılır.

Desteklenen kimlik bilgisi türlerini bildirme

res/xml dizininizde provider.xml adında yeni bir dosya oluşturun. Bu dosyada, kitaplıktaki her kimlik bilgisi türü için tanımlanan sabitlerle hizmetinizin desteklediği kimlik bilgisi türlerini tanımlayın. Aşağıdaki örnekte hizmet, geleneksel şifrelerin yanı sıra TYPE_PASSWORD_CREDENTIAL ve TYPE_PUBLIC_KEY_CREDENTIAL olarak tanımlanan geçiş anahtarlarını destekler:

<?xml version="1.0" encoding="utf-8"?>
<credential-provider xmlns:android="http://schemas.android.com/apk/res/android">
   <capabilities>
       <capability name="android.credentials.TYPE_PASSWORD_CREDENTIAL" />
       <capability name="androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL" />
   </capabilities>
</credential-provider>

Önceki API düzeylerinde kimlik bilgisi sağlayıcıları, şifreler ve diğer veriler için otomatik doldurma gibi API'lerle entegre olur. Bu sağlayıcılar, mevcut kimlik bilgisi türlerini depolamak için aynı dahili altyapıyı kullanabilir ve bunu geçiş anahtarları dahil olmak üzere diğerlerini destekleyecek şekilde genişletebilir.

Sağlayıcı etkileşimine iki aşamalı yaklaşım

Kimlik Bilgisi Yöneticisi, kimlik bilgisi sağlayıcılarıyla iki aşamada etkileşime geçer:

  1. İlk aşama başlatma/sorgu aşamasıdır. Bu aşamada sistem, kimlik bilgisi sağlayıcı hizmetlerine bağlanarak Begin… istekleriyle onBeginGetCredentialRequest(), onBeginCreateCredentialRequest() veya onClearCredentialStateRequest() yöntemlerini çağırır. Sağlayıcılar bu istekleri işlemeli ve hesap seçicide gösterilecek görsel seçenekleri temsil eden girişlerle doldurup Begin… yanıtlarıyla yanıtlamalıdır. Her girişte PendingIntent grubu olmalıdır.
  2. Kullanıcı bir giriş seçtiğinde seçim aşaması başlar ve girişle ilişkili PendingIntent etkinleşerek karşılık gelen sağlayıcı etkinliği gösterilir. Kullanıcı bu etkinlikle etkileşimde bulunmayı bitirdikten sonra, kimlik bilgisi sağlayıcısı etkinliği sonlandırmadan önce etkinliğin sonucuna verilen yanıtı ayarlamalıdır. Bu yanıt, daha sonra Kimlik Bilgisi Yöneticisi'ni çağıran istemci uygulamasına gönderilir.

Geçiş anahtarı oluşturma işlemini yönetin

Geçiş anahtarı oluşturma sorgularını işleme

İstemci uygulamaları geçiş anahtarı oluşturmak ve bunu bir kimlik bilgisi sağlayıcıda saklamak istediğinde createCredential API'yi çağırır. Bu isteği kimlik bilgisi sağlayıcı hizmetinizde, geçiş anahtarı aslında depolama alanınızda depolanacak şekilde işlemek için aşağıdaki bölümlerde gösterilen adımları tamamlayın.

  1. CredentialProviderService olan hizmetinizde onBeginCreateCredentialRequest() yöntemini geçersiz kılın.
  2. İlgili bir BeginCreateCredentialResponse oluşturup geri çağırma üzerinden ileterek BeginCreateCredentialRequest işini işleyin.
  3. BeginCreateCredentialResponse oluştururken gerekli CreateEntries değerini ekleyin. Her CreateEntry, kimlik bilgilerinin kaydedilebileceği bir hesaba karşılık gelmeli ve diğer gerekli meta verilerle birlikte bir PendingIntent içermelidir.

Aşağıdaki örnekte, bu adımların nasıl uygulanacağı gösterilmektedir.

override fun onBeginCreateCredentialRequest(
  request: BeginCreateCredentialRequest,
  cancellationSignal: CancellationSignal,
  callback: OutcomeReceiver<BeginCreateCredentialResponse, CreateCredentialException>,
) {
  val response: BeginCreateCredentialResponse? = processCreateCredentialRequest(request)
  if (response != null) {
    callback.onResult(response)
  } else {
    callback.onError(CreateCredentialUnknownException())
  }
}

fun processCreateCredentialRequest(request: BeginCreateCredentialRequest): BeginCreateCredentialResponse? {
  when (request) {
    is BeginCreatePublicKeyCredentialRequest -> {
      // Request is passkey type
      return handleCreatePasskeyQuery(request)
    }
  }
  // Request not supported
  return null
}

private fun handleCreatePasskeyQuery(
    request: BeginCreatePublicKeyCredentialRequest
    ): BeginCreateCredentialResponse {

    // Adding two create entries - one for storing credentials to the 'Personal'
    // account, and one for storing them to the 'Family' account. These
    // accounts are local to this sample app only.
    val createEntries: MutableList<CreateEntry> = mutableListOf()
    createEntries.add( CreateEntry(
        PERSONAL_ACCOUNT_ID,
        createNewPendingIntent(PERSONAL_ACCOUNT_ID, CREATE_PASSKEY_INTENT)
    ))

    createEntries.add( CreateEntry(
        FAMILY_ACCOUNT_ID,
        createNewPendingIntent(FAMILY_ACCOUNT_ID, CREATE_PASSKEY_INTENT)
    ))

    return BeginCreateCredentialResponse(createEntries)
}

private fun createNewPendingIntent(accountId: String, action: String): PendingIntent {
    val intent = Intent(action).setPackage(PACKAGE_NAME)

    // Add your local account ID as an extra to the intent, so that when
    // user selects this entry, the credential can be saved to this
    // account
    intent.putExtra(EXTRA_KEY_ACCOUNT_ID, accountId)

    return PendingIntent.getActivity(
        applicationContext, UNIQUE_REQ_CODE,
        intent, (
            PendingIntent.FLAG_MUTABLE
            or PendingIntent.FLAG_UPDATE_CURRENT
        )
    )
}

PendingIntent yapınız aşağıdakilere uygun olmalıdır:

  • İlgili etkinlik, gerekli biyometrik istemleri, onayları veya seçimleri gösterecek şekilde ayarlanmalıdır.
  • İlgili etkinlik çağrıldığında sağlayıcının ihtiyaç duyduğu tüm gerekli veriler, PendingIntent öğenizi oluşturmak için kullanılan amaçta ekstra bir veri olarak ayarlanmalıdır (oluşturma akışında accountId gibi).
  • Sistemin son isteği intent ekstrasına ekleyebilmesi için PendingIntent, PendingIntent.FLAG_MUTABLE işaretiyle oluşturulmalıdır.
  • PendingIntent öğesi, PendingIntent.FLAG_ONE_SHOT işaretiyle oluşturulmamalıdır. Kullanıcı bir giriş seçip geri dönüp yeniden seçebilir. Bu durumda PendingIntent, iki kez etkinleşir.
  • PendingIntent her girişin kendine karşılık gelen PendingIntent değerine sahip olabilmesi için benzersiz bir istek koduyla oluşturulmalıdır.

Geçiş anahtarı oluşturma istekleri için giriş seçimini işleme

  1. Kullanıcı daha önce doldurulmuş bir CreateEntry seçtiğinde karşılık gelen PendingIntent çağrılır ve ilişkili sağlayıcı Activity oluşturulur.
  2. Etkinliğinizin onCreate yöntemi çağrıldıktan sonra, ProviderCreateCredentialRequest almak için ilişkili amaca erişin ve bunu PendingIntentHander sınıfına geçirin.
  3. İstekten requestJson, callingAppInfo ve clientDataHash değerlerini çıkarın.
  4. Amaç ekstrasından yerel accountId çıkarın. Bu, uygulamaya özel örnek bir uygulamadır ve gerekli değildir. Bu hesap kimliği, bu kimlik bilgisini bu belirli hesap kimliğine göre depolamak için kullanılabilir.
  5. requestJson öğesini doğrulayın. Aşağıdaki örnekte, giriş JSON'ini WebAuthn spesifikasyonuna göre yapılandırılmış bir sınıfa dönüştürmek için PublicKeyCredentialCreationOptions gibi yerel veri sınıfları kullanılmaktadır. Bir kimlik bilgisi sağlayıcısı olarak bunu kendi ayrıştırıcınızla değiştirebilirsiniz.
  6. Çağrı, yerel bir Android uygulamasından geliyorsa çağrı yapan uygulamanın öğe bağlantısını kontrol edin.
  7. Bir kimlik doğrulama istemi gösterilir. Aşağıdaki örnekte Android Biometric API kullanılmıştır.
  8. Kimlik doğrulama başarılı olduğunda credentialId ve anahtar çifti oluşturun.
  9. Yerel veritabanınızdaki özel anahtarı callingAppInfo.packageName hesabına kaydedin.
  10. Ortak anahtar ve credentialId'dan oluşan bir Web Authentication API JSON yanıtı oluşturun. Aşağıdaki örnekte, daha önce belirtilen spesifikasyona göre bir JSON oluşturulmasına yardımcı olan AuthenticatorAttestationResponse ve FidoPublicKeyCredential gibi yerel yardımcı program sınıfları kullanılmaktadır.Bir kimlik bilgisi sağlayıcısı olarak bu sınıfları kendi oluşturucularınızla değiştirebilirsiniz.
  11. Yukarıda oluşturulan JSON dosyasıyla bir CreatePublicKeyCredentialResponse oluşturun.
  12. CreatePublicKeyCredentialResponse öğesini Intent ile PendingIntentHander.setCreateCredentialResponse() arasındaki bir öğede ekstra olarak ayarlayın ve bu niyeti Etkinliğin sonucuna ayarlayın.
  13. Etkinliği tamamlayın.

Aşağıdaki kod örneğinde bu adımlar gösterilmektedir. onCreate() çağrıldıktan sonra, bu kodun Etkinlik sınıfınızda işlenmesi gerekir.

val request =
  PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent)

val accountId = intent.getStringExtra(CredentialsRepo.EXTRA_KEY_ACCOUNT_ID)
if (request != null && request.callingRequest is CreatePublicKeyCredentialRequest) {
  val publicKeyRequest: CreatePublicKeyCredentialRequest =
    request.callingRequest as CreatePublicKeyCredentialRequest
  createPasskey(
    publicKeyRequest.requestJson,
    request.callingAppInfo,
    publicKeyRequest.clientDataHash,
    accountId
  )
}

fun createPasskey(
  requestJson: String,
  callingAppInfo: CallingAppInfo?,
  clientDataHash: ByteArray?,
  accountId: String?
) {
  val request = PublicKeyCredentialCreationOptions(requestJson)

  val biometricPrompt = BiometricPrompt(
    this,
    <executor>,
    object : BiometricPrompt.AuthenticationCallback() {
      override fun onAuthenticationError(
        errorCode: Int, errString: CharSequence
      ) {
        super.onAuthenticationError(errorCode, errString)
        finish()
      }

      override fun onAuthenticationFailed() {
        super.onAuthenticationFailed()
        finish()
      }

      override fun onAuthenticationSucceeded(
        result: BiometricPrompt.AuthenticationResult
      ) {
        super.onAuthenticationSucceeded(result)

        // Generate a credentialId
        val credentialId = ByteArray(32)
        SecureRandom().nextBytes(credentialId)

        // Generate a credential key pair
        val spec = ECGenParameterSpec("secp256r1")
        val keyPairGen = KeyPairGenerator.getInstance("EC");
        keyPairGen.initialize(spec)
        val keyPair = keyPairGen.genKeyPair()

        // Save passkey in your database as per your own implementation

        // Create AuthenticatorAttestationResponse object to pass to
        // FidoPublicKeyCredential

        val response = AuthenticatorAttestationResponse(
          requestOptions = request,
          credentialId = credentialId,
          credentialPublicKey = getPublicKeyFromKeyPair(keyPair),
          origin = appInfoToOrigin(callingAppInfo),
          up = true,
          uv = true,
          be = true,
          bs = true,
          packageName = callingAppInfo.packageName
        )

        val credential = FidoPublicKeyCredential(
          rawId = credentialId, response = response
        )
        val result = Intent()

        val createPublicKeyCredResponse =
          CreatePublicKeyCredentialResponse(credential.json())

        // Set the CreateCredentialResponse as the result of the Activity
        PendingIntentHandler.setCreateCredentialResponse(
          result, createPublicKeyCredResponse
        )
        setResult(Activity.RESULT_OK, result)
        finish()
      }
    }
  )

  val promptInfo = BiometricPrompt.PromptInfo.Builder()
    .setTitle("Use your screen lock")
    .setSubtitle("Create passkey for ${request.rp.name}")
    .setAllowedAuthenticators(
        BiometricManager.Authenticators.BIOMETRIC_STRONG
        /* or BiometricManager.Authenticators.DEVICE_CREDENTIAL */
      )
    .build()
  biometricPrompt.authenticate(promptInfo)
}

fun appInfoToOrigin(info: CallingAppInfo): String {
  val cert = info.signingInfo.apkContentsSigners[0].toByteArray()
  val md = MessageDigest.getInstance("SHA-256");
  val certHash = md.digest(cert)
  // This is the format for origin
  return "android:apk-key-hash:${b64Encode(certHash)}"
}

Şifre oluşturma istekleriyle ilgili sorguları işleyin

Şifre oluşturma istekleriyle ilgili sorguları işlemek için şunları yapın:

  • Önceki bölümde belirtilen processCreateCredentialRequest() yönteminizin içinde, şifre isteklerini ele almak için anahtar bloğunun içine başka bir büyük/küçük harf ekleyin.
  • BeginCreateCredentialResponse oluştururken gerekli CreateEntries ekleyin.
  • Her CreateEntry, kimlik bilgilerinin kaydedilebileceği bir hesaba karşılık gelmeli ve diğer meta verilerle birlikte bir PendingIntent içermelidir.

Aşağıdaki örnekte bu adımların nasıl uygulanacağı gösterilmektedir:

fun processCreateCredentialRequest(
    request: BeginCreateCredentialRequest
  ): BeginCreateCredentialResponse? {
  when (request) {
    is BeginCreatePublicKeyCredentialRequest -> {
      // Request is passkey type
      return handleCreatePasskeyQuery(request)
    }

    is BeginCreatePasswordCredentialRequest -> {
    // Request is password type
      return handleCreatePasswordQuery(request)
    }
  }
  return null
}

private fun handleCreatePasswordQuery(
    request: BeginCreatePasswordCredentialRequest
  ): BeginCreateCredentialResponse {
  val createEntries: MutableList<CreateEntry> = mutableListOf()

  // Adding two create entries - one for storing credentials to the 'Personal'
  // account, and one for storing them to the 'Family' account. These
  // accounts are local to this sample app only.
  createEntries.add(
    CreateEntry(
      PERSONAL_ACCOUNT_ID,
      createNewPendingIntent(PERSONAL_ACCOUNT_ID, CREATE_PASSWORD_INTENT)
    )
  )
  createEntries.add(
    CreateEntry(
      FAMILY_ACCOUNT_ID,
      createNewPendingIntent(FAMILY_ACCOUNT_ID, CREATE_PASSWORD_INTENT)
    )
  )

  return BeginCreateCredentialResponse(createEntries)
}

Şifre oluşturma istekleri için giriş seçimini işleme

Kullanıcı, doldurulmuş bir CreateEntry seçtiğinde karşılık gelen PendingIntent yürütülür ve ilişkili Etkinliği açar. onCreate politikasında iletilen ilişkili amaca erişin ve ProviderCreateCredentialRequest yöntemini almak için bunu PendingIntentHander sınıfına geçirin.

Aşağıdaki örnekte, bu sürecin nasıl uygulanacağı gösterilmektedir. Bu kodun, Etkinliğinizin onCreate() yönteminde işlenmesi gerekir.

val createRequest = PendingIntentHandler.retrieveProviderCreateCredentialRequest(intent)
val accountId = intent.getStringExtra(CredentialsRepo.EXTRA_KEY_ACCOUNT_ID)

val request: CreatePasswordRequest = createRequest.callingRequest as CreatePasswordRequest

// Fetch the ID and password from the request and save it in your database
<your_database>.addNewPassword(
    PasswordInfo(
        request.id,
        request.password,
        createRequest.callingAppInfo.packageName
    )
)

//Set the final response back
val result = Intent()
val response = CreatePasswordResponse()
PendingIntentHandler.setCreateCredentialResponse(result, response)
setResult(Activity.RESULT_OK, result)
this@<activity>.finish()

Kullanıcı oturum açma işlemini yönetin

Kullanıcı oturumu açma işlemi aşağıdaki adımlar uygulanarak gerçekleştirilir:

  • Bir istemci uygulaması kullanıcıda oturum açmaya çalıştığında bir GetCredentialRequest örneği hazırlar.
  • Android çerçevesi, bu hizmetlere bağlanarak bu isteği geçerli tüm kimlik bilgisi sağlayıcılarına yayar.
  • Daha sonra sağlayıcı hizmeti, her biri eşleşen kimlik bilgilerini almak için kullanılabilecek parametreler içeren BeginGetCredentialOption listesini içeren bir BeginGetCredentialRequest alır.

Bu isteği kimlik bilgisi sağlayıcı hizmetinizde işlemek için aşağıdaki adımları tamamlayın:

  1. İsteği işlemek için onBeginGetCredentialRequest() yöntemini geçersiz kılın. Kimlik bilgileriniz kilitliyse yanıtta hemen bir AuthenticationAction ayarlayıp geri çağırmayı çağırabileceğinizi unutmayın.

    private val unlockEntryTitle = "Authenticate to continue"
    
    override fun onBeginGetCredentialRequest(
        request: BeginGetCredentialRequest,
        cancellationSignal: CancellationSignal,
        callback: OutcomeReceiver<BeginGetCredentialResponse, GetCredentialException>,
    ) {
        if (isAppLocked()) {
            callback.onResult(BeginGetCredentialResponse(
                authenticationActions = mutableListOf(AuthenticationAction(
                    unlockEntryTitle, createUnlockPendingIntent())
                    )
                )
            )
            return
        }
        try {
            response = processGetCredentialRequest(request)
            callback.onResult(response)
        } catch (e: GetCredentialException) {
            callback.onError(GetCredentialUnknownException())
        }
    }
    

    Herhangi bir credentialEntries döndürmeden önce kimlik bilgilerinin kilidinin açılmasını gerektiren sağlayıcılar, kullanıcıyı uygulamanın kilit açma akışına yönlendiren beklemedeki bir amaç oluşturmalıdır:

    private fun createUnlockPendingIntent(): PendingIntent {
        val intent = Intent(UNLOCK_INTENT).setPackage(PACKAGE_NAME)
        return PendingIntent.getActivity(
        applicationContext, UNIQUE_REQUEST_CODE, intent, (
            PendingIntent.FLAG_MUTABLE
            or PendingIntent.FLAG_UPDATE_CURRENT
            )
        )
    }
    
  2. Kimlik bilgilerini yerel veritabanınızdan alın ve seçicide gösterilmek üzere CredentialEntries kullanarak ayarlayın. Geçiş anahtarları söz konusu olduğunda, kullanıcı bu girişi seçtiğinde hangi kimlik bilgisiyle eşlendiğini öğrenmek için credentialId özelliğini niyette ekstra bir öğe olarak ayarlayabilirsiniz.

    companion object {
        // These intent actions are specified for corresponding activities
        // that are to be invoked through the PendingIntent(s)
        private const val GET_PASSKEY_INTENT_ACTION = "PACKAGE_NAME.GET_PASSKEY"
        private const val GET_PASSWORD_INTENT_ACTION = "PACKAGE_NAME.GET_PASSWORD"
    
    }
    
    fun processGetCredentialsRequest(
    request: BeginGetCredentialRequest
    ): BeginGetCredentialResponse {
        val callingPackage = request.callingAppInfo?.packageName
        val credentialEntries: MutableList<CredentialEntry> = mutableListOf()
    
        for (option in request.beginGetCredentialOptions) {
            when (option) {
                is BeginGetPasswordOption -> {
                    credentialEntries.addAll(
                            populatePasswordData(
                                callingPackage,
                                option
                            )
                        )
                    }
                    is BeginGetPublicKeyCredentialOption -> {
                        credentialEntries.addAll(
                            populatePasskeyData(
                                callingPackage,
                                option
                            )
                        )
                    )
                } else -> {
                    Log.i(TAG, "Request not supported")
                }
            }
        }
        return BeginGetCredentialResponse(credentialEntries)
    }
    
  3. Veritabanınızdaki kimlik bilgilerini sorgulayın, geçiş anahtarı ve şifre girişleri oluşturun.

    private fun populatePasskeyData(
        callingAppInfo: CallingAppInfo,
        option: BeginGetPublicKeyCredentialOption
    ): List<CredentialEntry> {
      val passkeyEntries: MutableList<CredentialEntry> = mutableListOf()
      val request = PublicKeyCredentialRequestOptions(option.requestJson)
      // Get your credentials from database where you saved during creation flow
      val creds = <getCredentialsFromInternalDb(request.rpId)>
      val passkeys = creds.passkeys
      for (passkey in passkeys) {
          val data = Bundle()
          data.putString("credId", passkey.credId)
          passkeyEntries.add(
              PublicKeyCredentialEntry(
                  context = applicationContext,
                  username = passkey.username,
                  pendingIntent = createNewPendingIntent(
                      GET_PASSKEY_INTENT_ACTION,
                      data
                  ),
                  beginPublicKeyCredentialOption = option,
                  displayName = passkey.displayName,
                  icon = passkey.icon
              )
          )
      }
      return passkeyEntries
    }
    
    // Fetch password credentials and create password entries to populate to
    // the user
    private fun populatePasswordData(
    callingPackage: String,
    option: BeginGetPasswordOption
    ): List<CredentialEntry> {
        val passwordEntries: MutableList<CredentialEntry> = mutableListOf()
    
        // Get your password credentials from database where you saved during
        // creation flow
        val creds = <getCredentialsFromInternalDb(callingPackage)>
        val passwords = creds.passwords
        for (password in passwords) {
            passwordEntries.add(
                PasswordCredentialEntry(
                    context = applicationContext,
                    username = password.username,
                    pendingIntent = createNewPendingIntent(
                    GET_PASSWORD_INTENT
                    ),
                    beginGetPasswordOption = option
                        displayName = password.username,
                    icon = password.icon
                )
            )
        }
        return passwordEntries
    }
    
    private fun createNewPendingIntent(
        action: String,
        extra: Bundle? = null
    ): PendingIntent {
        val intent = Intent(action).setPackage(PACKAGE_NAME)
        if (extra != null) {
            intent.putExtra("CREDENTIAL_DATA", extra)
        }
    
        return PendingIntent.getActivity(
            applicationContext, UNIQUE_REQUEST_CODE, intent,
            (PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
        )
    }
    
  4. Kimlik bilgilerini sorgulayıp doldurduktan sonra, kullanıcı tarafından seçilen kimlik bilgileri (geçiş anahtarı veya şifre) için seçim aşamasını tamamlamanız gerekir.

Geçiş anahtarları için kullanıcı seçimini işleme

  1. İlgili Etkinliğin onCreate yönteminde, ilişkili amacı alın ve PendingIntentHandler.retrieveProviderGetCredentialRequest()'e iletin.
  2. Yukarıda aldığınız istekten GetPublicKeyCredentialOption kodunu çıkarın. Ardından, bu seçenekten requestJson ve clientDataHash değerlerini ayıklayın.
  3. İlgili PendingIntent ayarlanırken kimlik bilgisi sağlayıcı tarafından doldurulan intent ekstrasından credentialId değerini çıkarın.
  4. Yukarıda erişilen istek parametrelerini kullanarak geçiş anahtarını yerel veritabanınızdan çıkarın.
  5. Alınan meta veriler ve kullanıcı doğrulamasıyla geçiş anahtarının geçerli olduğunu doğrulayın.

    val getRequest =
        PendingIntentHandler.retrieveProviderGetCredentialRequest(intent)
    val publicKeyRequest =
    getRequest.credentialOption as GetPublicKeyCredentialOption
    
    val requestInfo = intent.getBundleExtra("CREDENTIAL_DATA")
    val credIdEnc = requestInfo.getString("credId")
    
    // Get the saved passkey from your database based on the credential ID
    // from the publickeyRequest
    val passkey = <your database>.getPasskey(credIdEnc)
    
    // Decode the credential ID, private key and user ID
    val credId = b64Decode(credIdEnc)
    val privateKey = b64Decode(passkey.credPrivateKey)
    val uid = b64Decode(passkey.uid)
    
    val origin = appInfoToOrigin(getRequest.callingAppInfo)
    val packageName = getRequest.callingAppInfo.packageName
    
    validatePasskey(
        publicKeyRequest.requestJson,
        origin,
        packageName,
        uid,
        passkey.username,
        credId,
        privateKey
    )
    
  6. Kullanıcıyı doğrulamak için bir Biyometrik istem (veya başka bir onaylama yöntemi) gösterin. Aşağıdaki kod snippet'i Android Biometrik API'sını kullanmaktadır.

  7. Kimlik doğrulama başarılı olduğunda W3 Web Kimlik Doğrulaması Onayı spesifikasyonuna göre bir JSON yanıtı oluşturun. Aşağıdaki kod snippet'inde, yapılandırılmış parametreleri almak ve bunları gerekli JSON biçimine dönüştürmek için AuthenticatorAssertionResponse gibi yardımcı veri sınıfları kullanılmıştır. Yanıt, WebAuthn kimlik bilgisinin özel anahtarından bir dijital imza içerir. Bağlı tarafın sunucusu, oturum açmadan önce kullanıcının kimliğini doğrulamak için bu imzayı doğrulayabilir.

  8. Yukarıda oluşturulan JSON dosyasını kullanarak bir PublicKeyCredential oluşturun ve son bir GetCredentialResponse üzerinde ayarlayın. Etkinliğin sonucuna göre bu son yanıtı ayarlayın.

Aşağıdaki örnekte bu adımların nasıl uygulanabileceği gösterilmektedir:

val request = PublicKeyCredentialRequestOptions(requestJson)
val privateKey: ECPrivateKey = convertPrivateKey(privateKeyBytes)

val biometricPrompt = BiometricPrompt(
    this,
    <executor>,
    object : BiometricPrompt.AuthenticationCallback() {
        override fun onAuthenticationError(
        errorCode: Int, errString: CharSequence
        ) {
            super.onAuthenticationError(errorCode, errString)
            finish()
        }

        override fun onAuthenticationFailed() {
            super.onAuthenticationFailed()
            finish()
        }

        override fun onAuthenticationSucceeded(
        result: BiometricPrompt.AuthenticationResult
        ) {
        super.onAuthenticationSucceeded(result)
        val response = AuthenticatorAssertionResponse(
            requestOptions = request,
            credentialId = credId,
            origin = origin,
            up = true,
            uv = true,
            be = true,
            bs = true,
            userHandle = uid,
            packageName = packageName
        )

        val sig = Signature.getInstance("SHA256withECDSA");
        sig.initSign(privateKey)
        sig.update(response.dataToSign())
        response.signature = sig.sign()

        val credential = FidoPublicKeyCredential(
            rawId = credId, response = response
        )
        val result = Intent()
        val passkeyCredential = PublicKeyCredential(credential.json)
        PendingIntentHandler.setGetCredentialResponse(
            result, GetCredentialResponse(passkeyCredential)
        )
        setResult(RESULT_OK, result)
        finish()
        }
    }
)

val promptInfo = BiometricPrompt.PromptInfo.Builder()
    .setTitle("Use your screen lock")
    .setSubtitle("Use passkey for ${request.rpId}")
    .setAllowedAuthenticators(
            BiometricManager.Authenticators.BIOMETRIC_STRONG
            /* or BiometricManager.Authenticators.DEVICE_CREDENTIAL */
        )
    .build()
biometricPrompt.authenticate(promptInfo)

Şifre kimlik doğrulaması için kullanıcı seçimini işleme

  1. İlgili etkinliğinizde, onCreate işlevine iletilen amaca erişin ve PendingIntentHandler kullanarak ProviderGetCredentialRequest öğesini çıkarın.
  2. Gelen paket adı için şifre kimlik bilgilerini almak üzere istekte GetPasswordOption kullanın.

    val getRequest =
    PendingIntentHandler.retrieveProviderGetCredentialRequest(intent)
    
    val passwordOption = getRequest.credentialOption as GetPasswordCredentialOption
    
    val username = passwordOption.username
    // Fetch the credentials for the calling app package name
    val creds = <your_database>.getCredentials(callingAppInfo.packageName)
    val passwords = creds.passwords
    val it = passwords.iterator()
    var password = ""
    while (it.hasNext() == true) {
        val passwordItemCurrent = it.next()
        if (passwordItemCurrent.username == username) {
           password = passwordItemCurrent.password
           break
        }
    }
    
  3. Alındığında, seçilen şifre kimlik bilgilerinin yanıtını ayarlayın.

    // Set the response back
    val result = Intent()
    val passwordCredential = PasswordCredential(username, password)
    PendingIntentHandler.setGetCredentialResponse(
    result, GetCredentialResponse(passwordCredential)
    )
    setResult(Activity.RESULT_OK, result)
    finish()
    

Kimlik doğrulama işlemi girişi seçimini işleme

Daha önce de belirtildiği gibi, kimlik bilgileri kilitliyse kimlik bilgileri sağlayıcısı bir AuthenticationAction ayarlayabilir. Kullanıcı bu girişi seçerse PendingIntent içinde ayarlanan intent işlemine karşılık gelen Activity çağrılır. Kimlik bilgisi sağlayıcılar, kimlik bilgilerinin kilidini açmak için biyometrik kimlik doğrulama akışı veya benzer bir mekanizma kullanabilir. Başarılı olursa kimlik bilgilerinin kilidi açılmış olduğundan kimlik bilgisi sağlayıcısı, yukarıda açıklanan kullanıcı oturum açma işlemine benzer şekilde bir BeginGetCredentialResponse oluşturmalıdır. Ardından, hazırlanan amacın sonuç olarak ayarlanmasından ve Etkinlik tamamlanmadan önce bu yanıtın PendingIntentHandler.setBeginGetCredentialResponse() yöntemiyle ayarlanması gerekir.

Kimlik bilgisi isteklerini temizle

Bir istemci uygulaması, kimlik bilgisi seçimi için korunan herhangi bir durumun temizlenmesini isteyebilir. Örneğin, bir kimlik bilgisi sağlayıcısı daha önce seçilen kimlik bilgilerini hatırlayabilir ve bu durumu yalnızca bir sonraki sefer geri gönderebilir. Bir istemci uygulaması bu API'yi çağırır ve yapışkan seçimin silinmesini bekler. Kimlik bilgisi sağlayıcı hizmetiniz onClearCredentialStateRequest() yöntemini geçersiz kılarak bu isteği işleyebilir:

override fun onClearCredentialStateRequest(
    request: android.service.credentials.ClearCredentialStateRequest,
    cancellationSignal: CancellationSignal,
    callback: OutcomeReceiver<Void?, ClearCredentialException>,
  ) {
    // Delete any maintained state as appropriate.
}

Ayrıcalıklı uygulamaların izin verilenler listesini alma

Web tarayıcıları gibi ayrıcalıklı uygulamalar, Kimlik Bilgisi Yöneticisi GetCredentialRequest() ve CreatePublicKeyCredentialRequest() yöntemlerinde origin parametresini ayarlayarak diğer bağlı taraflar adına Kimlik Bilgisi Yöneticisi çağrıları yapar. Kimlik bilgisi sağlayıcısı, bu istekleri işlemek için getOrigin() API'sini kullanarak origin bilgisini alır.

origin bilgisini almak için kimlik bilgisi sağlayıcı uygulamasının, ayrıcalıklı ve güvenilir arayanlar listesini androidx.credentials.provider.CallingAppInfo's getOrigin() API'ye aktarması gerekir. Bu izin verilenler listesi geçerli bir JSON nesnesi olmalıdır. packageName ve signingInfo üzerinden alınan sertifika parmak izleri, getOrigin() API'ye aktarılan privilegedAllowlist'deki bir uygulamanın parmak izleriyle eşleşirse origin döndürülür. origin değeri elde edildikten sonra, sağlayıcı uygulaması bunu ayrıcalıklı bir çağrı olarak değerlendirmeli ve origin özelliğini çağıran uygulamanın imzasını kullanarak hesaplamak yerine AuthenticatorResponse içindeki istemci verilerinde bu origin özelliğini ayarlamalıdır.

Bir origin alırsanız imza isteği sırasında derleme ve karma oluşturma işlemi uygulamak yerineclientDataJSON doğrudan CreatePublicKeyCredentialRequest() veya GetPublicKeyCredentialOption() içinde sağlanan clientDataHash kodunu kullanın. JSON ayrıştırma sorunlarını önlemek amacıyla onay ve onaylama yanıtında clientDataJSON için bir yer tutucu değeri ayarlayın.

Google Şifre Yöneticisi, getOrigin() çağrıları için herkese açık bir izin verilenler listesi kullanır. Bir kimlik bilgisi sağlayıcısı olarak, bu listeyi kullanabilir veya API tarafından açıklanan JSON biçiminde kendi kimlik bilgilerinizi sağlayabilirsiniz. Hangi listenin kullanılacağını seçmek sağlayıcıya bağlıdır. Üçüncü taraf kimlik bilgisi sağlayıcılarıyla ayrıcalıklı erişim elde etmek için, üçüncü tarafın sağladığı belgelere bakın.

Cihazda sağlayıcıları etkinleştirme

Kullanıcılar, sağlayıcıyı cihaz ayarları > Şifreler ve Hesaplar > Sağlayıcınız > Etkinleştir veya Devre Dışı Bırak üzerinden etkinleştirmelidir.

Android 14 veya sonraki sürümlerde, çağrıldığında bekleyen bir niyetin döndürülmesi için createSettingsPendingIntent() API'yi çağırın ve kullanıcının Kimlik Bilgisi Yöneticisi sağlayıcınızı etkinleştirmesine izin veren bir ekran gösterilir.

fun createSettingsPendingIntent(): PendingIntent