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 birden fazla oturum açma yöntemini destekleyen bir API grubudur. Credential Manager API çağrılırsa Android sistemi, tüm kimlik bilgilerindeki kimlik bilgilerini toplar. sağlayıcıda yüklü olup olmadığını kontrol edin. Bu belgede, bu kimlik bilgisi sağlayıcılar için entegrasyon uç noktaları sağlayan API grubu açıklanmaktadır.

Kurulum

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

Bağımlılıkları belirtme

Modülünüzün build.gradle dosyasında en son sürümünü eklemeniz gerekir:

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

Manifest dosyasında hizmet öğesini bildir

Uygulamanızın AndroidManifest.xml manifest dosyasına bir <service> ekleyin kapsamını genişleten bir hizmet sınıfı için androidx.credentials kitaplığından CredentialProviderService sınıfını, aşağıdaki örnekte gösterildiği gibidir.

<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 için dahilidir Yönetici akışının beklendiği gibi çalışması. Yalnızca Android sistemi bu hizmete bağlanabilir. Intent filtresi, Kimlik Bilgileri Yöneticisi tarafından kullanılacak bir kimlik bilgisi sağlayıcı olarak bu hizmetin bulunabilirliği için kullanılır.

Desteklenen kimlik bilgisi türlerini bildirme

res/xml dizininizde provider.xml adlı yeni bir dosya oluşturun. Bu dosyada, hizmetinizin desteklediği kimlik bilgisi türlerini, kitaplıktaki her kimlik bilgisi türü için tanımlanan sabitler aracılığıyla belirtin. Sonraki Örneğin hizmet, geleneksel şifrelerin yanı sıra geçiş anahtarlarını değer olarak tanımlanan sabit değerler TYPE_PASSWORD_CREDENTIAL ve TYPE_PUBLIC_KEY_CREDENTIAL:

<?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 geçiş anahtarları da dahil olmak üzere diğer kimlik bilgisi türlerini desteklemek için bu altyapıyı genişletebilir.

Sağlayıcı etkileşimi için iki aşamalı yaklaşım

Kimlik Bilgisi Yöneticisi, kimlik bilgisi sağlayıcılarla iki aşamada etkileşim kurar:

  1. İlk aşama başlangıç/sorgu aşamasıdır. Bu aşamada sistemin kimlik bilgisi sağlayıcı hizmetleri ve çağrıları onBeginGetCredentialRequest(), onBeginCreateCredentialRequest() veya onClearCredentialStateRequest() yöntemi, Begin… isteği ile gerçekleştiriliyor. Sağlayıcılar bu istekleri işlemeli ve Begin… yanıtla yanıtlamalıdır. gösterilecek görsel seçenekleri temsil eden girişlerle doldurmak hesap seçicide. Her girişte bir 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 tetiklendiğinde, sağlayıcı etkinlikleri olabilir. Kullanıcı bu etkinlikle etkileşimi sonlandırdıktan sonra kimlik bilgisi sağlayıcı, etkinliği sonlandırmadan önce yanıtı etkinliğin sonucuna göre ayarlamalıdır. Bu yanıt, daha sonra şu istemciyi kullanan istemci uygulamasına gönderilir: çağrılır.

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

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

Bir istemci uygulaması geçiş anahtarı oluşturmak ve bunu bir kimlik bilgisi sağlayıcılarını createCredential API'yi çağırır. Bunu yapmak için kimlik bilgisi sağlayıcı hizmetinizde, geçiş anahtarının gerçekten için aşağıdaki bölümlerde gösterilen adımları tamamlayın.

  1. Hizmetinizde onBeginCreateCredentialRequest() yöntemini geçersiz kılın CredentialProviderService tarihinden itibaren uzatıldı.
  2. BeginCreateCredentialRequest için BeginCreateCredentialResponse anahtar kelimesine karşılık gelen ve bunu geri arama.
  3. BeginCreateCredentialResponse oluştururken CreateEntries zorunlu. Her CreateEntry, kimlik bilgisinin kaydedilebileceği bir hesaba karşılık gelmelidir ve gerekli diğer meta verilerle birlikte bir PendingIntent değerine sahip olmalıdır.

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ısı aşağıdaki şartlara uygun olmalıdır:

  • İlgili Etkinlik, gerekli olabilecek tüm olası satışları ortaya çıkaracak şekilde Biyometrik istem, onay veya seçim gerekiyor.
  • İlgili etkinlik olduğunda sağlayıcının ihtiyaç duyduğu tüm gerekli veriler oluşturmak için kullanılan niyete ek olarak PendingIntent (ör. oluşturma akışında accountId).
  • Sistemin nihai isteği intent ekstrasına ekleyebilmesi için PendingIntent, PendingIntent.FLAG_MUTABLE işaretiyle oluşturulmalıdır.
  • Kullanıcı bir girişi seçip geri dönüp yeniden seçebileceğinden PendingIntent, PendingIntent.FLAG_ONE_SHOT işaretiyle oluşturulmamalıdır. Bu durumda PendingIntent iki kez tetiklenir.
  • Her girişin kendi PendingIntent değerine sahip olabilmesi için PendingIntent değeriniz 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, ilgili 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 PendingIntentHander sınıfına iletir. Böylece, ProviderCreateCredentialRequest.
  3. Şu konumdan requestJson, callingAppInfo ve clientDataHash'yi çıkarın: isteği gönderin.
  4. Amaç ekstrasından yerel accountId'yi çıkarın. Bu örnek bir uygulamadır uygulanması gerekli değildir. Bu hesap kimliği, kimlik bilgisini bu hesap kimliğine göre depolamak için kullanılabilir.
  5. requestJson öğesini doğrulayın. Aşağıdaki örnekte, giriş JSON'unu 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. Kimlik bilgisi sağlayıcı olarak bunu kendi ayrıştırıcınızla değiştirebilirsiniz.
  6. Arama, yerel bir Android uygulamasından geliyorsa arayan uygulamanın asset-link değerini kontrol edin.
  7. Kimlik doğrulama istemi gösterin. Aşağıdaki örnekte Android Biometric API'si kullanılmaktadır.
  8. Kimlik doğrulama başarılı olduğunda bir credentialId ve anahtar çifti oluşturun.
  9. Özel anahtarı yerel veritabanınızda callingAppInfo.packageName ile ilişkili olarak kaydedin.
  10. Aşağıdaki özelliklere sahip bir Web Authentication API JSON yanıtı: ortak anahtar ve credentialId'dan oluşur. Aşağıdaki örnekte, daha önce bahsedilen spesifikasyona göre JSON oluşturmaya yardımcı olan AuthenticatorAttestationResponse ve FidoPublicKeyCredential gibi yerel yardımcı sınıflar kullanılmaktadır. Kimlik bilgisi sağlayıcı olarak bu sınıfları kendi oluşturucularınızla değiştirebilirsiniz.
  11. Oluşturulan JSON ile bir CreatePublicKeyCredentialResponse oluşturun bölümünü ziyaret edin.
  12. PendingIntentHander.setCreateCredentialResponse() aracılığıyla CreatePublicKeyCredentialResponse'ü bir Intent'ta ekstra olarak ayarlayın ve bu intent'i Etkinlik'in sonucu olarak ayarlayın.
  13. Etkinliği tamamlayın.

Aşağıdaki kod örneğinde bu adımlar gösterilmektedir. Bu kodun şurada işlenmesi gerekiyor: onCreate() çağrıldıktan sonra Etkinlik sınıfınız.

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 istekleri için sorguları işleme

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

  • Şurada bahsedilen processCreateCredentialRequest() yönteminizin içinde: önceki bölümde, kullanım için anahtar bloğunun içine başka bir vaka ekleyin şifre istekleri.
  • BeginCreateCredentialResponse oluştururken gerekli CreateEntries ekleyin.
  • Her CreateEntry, kimlik bilgisinin kaydedilebileceği bir hesaba karşılık gelmelidir ve diğer meta verilerle birlikte bir PendingIntent değerine sahip olmalıdır.

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, ilgili PendingIntent yürütülür ve ilişkili etkinlik gösterilir. onCreate içinde iletilen ilişkili intent'e erişin ve ProviderCreateCredentialRequest yöntemini almak için PendingIntentHander sınıfına iletin.

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şlemlerini yönetme

Kullanıcı oturum açma işlemi aşağıdaki adımlarla gerçekleştirilir:

  • Bir istemci uygulaması bir kullanıcının oturumunu açmaya çalıştığında, GetCredentialRequest örneği.
  • Android çerçevesi, bu isteği geçerli tüm kimlik bilgilerine yayar bu hizmetlere bağlanarak.
  • Sağlayıcı hizmeti,BeginGetCredentialRequest her biri ayrı parametreler içeren BeginGetCredentialOption listesi eşleşen kimlik bilgilerini almak için kullanılabilir.

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 ayarlayabileceğinizi ve geri aramayı tetikleyebileceğ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 bekleyen bir intent 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. Yerel veritabanınızdan kimlik bilgilerini alın ve şunları kullanarak ayarlayın: CredentialEntries, seçicide gösterilecek. Geçiş anahtarları için şunları ayarlayabilirsiniz: Hangi kimlik bilgisini bilmek için istekte ekstra olarak credentialId kullanıcının bu girişi seçtiği zamana eşlenir.

    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ında kimlik bilgilerini sorgulayın, doldurmak için 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, artık kullanıcı tarafından seçilen kimlik bilgilerinin seçim aşaması geçiş anahtarı veya şifredir.

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

  1. İlgili Etkinlik'in onCreate yönteminde ilişkili intent'i alın ve PendingIntentHandler.retrieveProviderGetCredentialRequest()'ye iletin.
  2. Yukarıda alınan istekten GetPublicKeyCredentialOption öğesini ayıklayın. Daha sonra, requestJson ve clientDataHash verilerini çıkarın seçim yapabilirsiniz.
  3. İlgili PendingIntent ayarlandığında kimlik bilgisi sağlayıcı tarafından doldurulan intent ekstrasından credentialId öğesini çıkarın.
  4. İstek parametrelerini kullanarak geçiş anahtarını yerel veritabanınızdan çıkarın (yukarıdan erişilmiş)
  5. Ayıklanan 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 onay) gösterin yöntemi). Aşağıdaki kod snippet'inde Android Biometric API kullanılmaktadır.

  7. Kimlik doğrulama başarılı olduktan sonra, doğrulama gerçekleştirmek için W3 Web Authentication Assertion spec (Web Kimlik Doğrulaması Onaylama özellikleri). Kod snippet'inde aşağıda açıklandığı gibi AuthenticatorAssertionResponse gibi yardımcı veri sınıfları yapılandırılmış parametreleri alıp gerekli JSON biçimine dönüştürür biçimindedir. Yanıt, WebAuthn kimlik bilgisinin özel anahtarından alınan bir dijital imza içerir. Bağlı tarafın sunucusu doğrulama yapabilir kullanıcının kimliğini doğrulamak için bu imzayı kullanmanız gerekir.

  8. Yukarıda oluşturulan JSON dosyasını kullanarak bir PublicKeyCredential oluşturun ve son bir GetCredentialResponse olarak ayarlayın. Bu son yanıtı şu tarihte ayarla: bu etkinliğin bir sonucu olarak görülecek.

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'e iletilen intent'e erişin ve PendingIntentHandler kullanarak ProviderGetCredentialRequest'yi ayıklayın.
  2. Şifre alma isteğinde GetPasswordOption kullan kimlik bilgilerini girin.

    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ıktan sonra, seçilen şifre kimlik bilgisi için yanıtı 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şinin seçimini işleme

Daha önce de belirtildiği gibi, kimlik bilgisi sağlayıcı, Kimlik bilgileri kilitliyse AuthenticationAction. Kullanıcı bu girişi seçerse PendingIntent içinde ayarlanan intent işlemine karşılık gelen etkinlik çağrılır. Kimlik bilgisi sağlayıcıları, daha sonra biyometriyi gösterebilir kimlik doğrulama akışı veya benzeri mekanizma. Başarıda, Kimlik bilgisi sağlayıcı, BeginGetCredentialResponse, benzer bir yukarıda açıklandığı gibi (kimlik bilgileri artık yalnızca kilidi açıldı. Bu yanıt, daha sonra PendingIntentHandler.setBeginGetCredentialResponse() yöntemi önce Sonuç olarak hazırlanan amaç ayarlanır ve etkinlik tamamlanır.

Kimlik bilgisi isteklerini temizleme

Bir istemci uygulaması, kimlik bilgisi seçimi için korunan herhangi bir eyaletin olabilir; örneğin, kimlik bilgisi sağlayıcı daha önce seçilen yalnızca bir dahaki sefere bu bilgileri döndürür. Bir istemci uygulaması bu API'yi çağırır ve sabit seçimin temizlenmesini bekler. Yeterlilik Belgesi sağlayıcı hizmetiniz geçersiz kılarak bu isteği işleyebilir. onClearCredentialStateRequest() yöntemi:

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

Kullanıcılarınızın Şifreler, geçiş anahtarları ve otomatik doldurma ekranından sağlayıcınızın ayarlarını açmasına izin vermek için kimlik bilgisi sağlayıcı uygulamaları, res/xml/provider.xml içinde credential-provider settingsActivity manifest özelliğini uygulamalıdır. Bu özellik, kullanıcı Şifreler, geçiş anahtarları ve otomatik doldurma hizmet listesinde bir sağlayıcı adını tıkladığında uygulamanızın kendi ayarlar ekranını açmak için bir intent kullanmanıza olanak tanır. Bu özelliğin değerini Ayarlar ekranından başlatılacak etkinliğin adı.

<credential-provider
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:settingsSubtitle="Example settings provider name"
    android:settingsActivity="com.example.SettingsActivity">
    <capabilities>
        <capability name="android.credentials.TYPE_PUBLIC_KEY_CREDENTIAL" />
    </capabilities>
</credential-provider>
Değiştir ve aç düğmesi işlevlerini gösteren şema
Şekil 1: Değiştir düğmesi, mevcut seçim iletişim kutusunu açarak kullanıcının tercih ettiği kimlik bilgisi sağlayıcıyı seçmesine olanak tanır. düğmesi, bunun için özel olarak bir ayarlar sayfası açar. sağlayıcı.

Ayarlar amaçları

Ayarları açın: android.settings.CREDENTIAL_PROVIDER kullanıcının tercih ettiği ve istediği öğeyi seçebileceği bir ayarlar ekranı ek kimlik bilgisi sağlayıcıları.

Şifreler, geçiş anahtarları ve otomatik doldurma ayarları ekranı
Şekil 2: Şifreler, geçiş anahtarları ve otomatik doldurma ayarları dokunun.

Tercih edilen kimlik bilgisi hizmeti: ACTION_REQUEST_SET_AUTOFILL_SERVICE intent, kullanıcılarınızı sağlayıcı seçim ekranı görünür. Bu ekranda seçilen sağlayıcı tercih edilen kimlik bilgileri ve otomatik doldurma sağlayıcısı olur.

Değiştir ve aç düğmesi işlevlerini gösteren şema
Şekil 3: Şifreler, geçiş anahtarları, ve otomatik doldurma ayarları ekranını açın.

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 güvenen taraflar adına Kimlik Bilgisi Yöneticisi çağrıları yapar. Bu talepleri işleme almak için Kimlik bilgisi sağlayıcı, origin öğesini getOrigin() kullanarak alır API'ye gidin.

origin öğesini almak için kimlik bilgisi sağlayıcı uygulamasının arayanların ortak, ayrıcalıklı ve güvenilir arayan androidx.credentials.provider.CallingAppInfo's getOrigin() API'sı. Bu izin verilenler listesi geçerli bir JSON nesnesi olmalıdır. packageName ve signingInfo'den elde edilen sertifika parmak izleri, getOrigin() API'ye iletilen privilegedAllowlist'da bulunan bir uygulamanın parmak izleriyle eşleşirse origin döndürülür. origin değeri alındı, sağlayıcı uygulaması bunu ayrıcalıklı bir değer olarak kabul etmelidir çağrısı yapın ve origin istemci verilerinde ayarlayın AuthenticatorResponse içinde origin (telefon görüşmesi uygulamasının imzasını kullanarak).

Bir origin alırsanız doğrudan sağlanan clientDataHash öğesini kullanın CreatePublicKeyCredentialRequest() içinde veya derlemek ve karma oluşturma işlemi yapmak yerine GetPublicKeyCredentialOption() clientDataJSON hatası verdi. JSON ayrıştırma sorunlarını önlemek için doğrulama ve beyan yanıtında clientDataJSON için bir yer tutucu değer ayarlayın. Google Şifre Yöneticisi, getOrigin() çağrıları için herkese açık bir izin verilenler listesi kullanır. Kimlik bilgisi sağlayıcı olarak bu listeyi kullanabilir veya API tarafından açıklanan JSON biçiminde kendi listenizi sağlayabilirsiniz. Hangi listenin kullanılacağını belirleme hakkı sağlayıcıya aittir. Üçüncü taraf kimlik bilgisi sağlayıcılarla ayrıcalıklı erişim elde etmek için üçüncü taraf tarafından sağlanan dokümanlara bakın.

Bir cihazda sağlayıcıları etkinleştirme

Kullanıcıların, sağlayıcıyı şuradan etkinleştirmesi gerekir: cihaz ayarları > Şifreler ve Hesaplar > Sağlayıcınız > Etkinleştirin veya Devre Dışı Bırakın.

fun createSettingsPendingIntent(): PendingIntent