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 API grubudur. Credential Manager API çağrıldığında Android sistemi, cihazda yüklü olan tüm kimlik bilgisi sağlayıcılardan kimlik bilgilerini toplar. Bu dokümanda, bu kimlik bilgisi sağlayıcılar için entegrasyon uç noktaları sağlayan API'ler açıklanmaktadır.
Kurulum
Kimlik bilgisi sağlayıcınızda işlevleri uygulamadan önce aşağıdaki bölümlerde gösterilen kurulum adımlarını tamamlayın.
Bağımlılıkları bildirme
Kimlik Bilgisi Yöneticisi kitaplığının en yeni sürümünü kullanmak için uygulama modülünüzün derleme komut dosyasına aşağıdaki bağımlılıkları ekleyin:
Kotlin
dependencies { implementation("androidx.credentials:credentials:1.7.0-alpha02") }
Groovy
dependencies { implementation "androidx.credentials:credentials:1.7.0-alpha02" }
Manifest dosyasında hizmet öğesini bildirme
Uygulamanızın manifest dosyasında AndroidManifest.xml, androidx.credentials kitaplığındaki CredentialProviderService sınıfını genişleten bir hizmet sınıfı için <service> beyanı ekleyin. Aşağıdaki örnekte bu gösterilmektedir.
<service android:name=".MyCredentialProviderService"
android:enabled="true"
android:exported="true"
android:label="My Credential Provider"
android:icon="@mipmap/ic_launcher"
android:permission="android.permission.BIND_CREDENTIAL_PROVIDER_SERVICE"
tools:targetApi="upside_down_cake">
<intent-filter>
<action android:name="android.service.credentials.CredentialProviderService"/>
</intent-filter>
<meta-data
android:name="android.credentials.provider"
android:resource="@xml/provider"/>
</service>
Önceki örnekte gösterilen izin ve intent filtresi, Kimlik Bilgisi Yöneticisi akışının beklendiği gibi çalışması için gereklidir. Bu izin, yalnızca Android sisteminin bu hizmete bağlanabilmesi için gereklidir. Intent filtresi, bu hizmetin Kimlik Bilgisi Yöneticisi tarafından kullanılacak bir kimlik bilgisi sağlayıcı olarak 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, kitaplıktaki her kimlik bilgisi türü için tanımlanan sabitler aracılığıyla hizmetinizin desteklediği kimlik bilgisi türlerini bildirin. Aşağıdaki örnekte hizmet, geleneksel şifrelerin yanı sıra geçiş anahtarlarını da desteklemektedir. Bunların sabitleri TYPE_PASSWORD_CREDENTIAL ve TYPE_PUBLIC_KEY_CREDENTIAL olarak tanımlanmıştır:
<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 destekleyecek şekilde genişletebilir.
Sağlayıcı etkileşimine yönelik iki aşamalı yaklaşım
Kimlik Bilgisi Yöneticisi, kimlik bilgisi sağlayıcılarla iki aşamada etkileşim kurar:
- İlk aşama, sistemin kimlik bilgisi sağlayıcı hizmetlerine bağlandığı ve
Begin…istekleriyleonBeginGetCredentialRequest(),onBeginCreateCredentialRequest()veyaonClearCredentialStateRequest()yöntemlerini çağırdığı başlangıç/sorgu aşamasıdır. Sağlayıcılar bu istekleri işlemeli veBegin…yanıtlarıyla yanıt vermelidir. Bu yanıtlar, hesap seçicide gösterilecek görsel seçenekleri temsil eden girişlerle doldurulmalıdır. Her giriş için birPendingIntentayarlanmalıdır. - Kullanıcı bir giriş seçtiğinde seçim aşaması başlar ve girişle ilişkili
PendingIntenttetiklenerek ilgili sağlayıcı etkinliği gösterilir. Kullanıcı bu etkinlikle etkileşimi tamamladıktan sonra kimlik bilgisi sağlayıcı, etkinliği sonlandırmadan önce yanıtı etkinliğin sonucuna 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önetme
Geçiş anahtarı oluşturma sorgularını işleme
İstemci uygulaması geçiş anahtarı oluşturmak ve bunu bir kimlik bilgisi sağlayıcıda depolamak istediğinde createCredential API'sini çağırır. Bu isteği, geçiş anahtarının depolama alanınızda saklanacağı şekilde kimlik bilgisi sağlayıcı hizmetinizde işlemek için aşağıdaki bölümlerde gösterilen adımları tamamlayın.
CredentialProviderServicesınıfından genişletilen hizmetinizdeonBeginCreateCredentialRequest()yöntemini geçersiz kılın.- İlgili
BeginCreateCredentialResponseöğesini oluşturup geri çağırma üzerinden ileterekBeginCreateCredentialRequestöğesini işleyin. BeginCreateCredentialResponseoluştururken gerekliCreateEntriesekleyin. HerCreateEntry, kimlik bilgilerinin kaydedilebileceği bir hesaba karşılık gelmeli ve diğer gerekli meta verilerle birlikte ayarlanmış birPendingIntentdeğ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ınız aşağıdakilere uygun olmalıdır:
- İlgili etkinlik, gerekli biyometrik istemi, onayı veya seçimi gösterecek şekilde ayarlanmalıdır.
- İlgili etkinlik çağrıldığında sağlayıcının ihtiyaç duyduğu tüm gerekli veriler, oluşturma akışındaki
accountIdgibiPendingIntentöğenizi oluşturmak için kullanılan intent'e ek olarak ayarlanmalıdır. - Sistemin nihai isteği intent ekine ekleyebilmesi için
PendingIntentöğenizPendingIntent.FLAG_MUTABLEişaretiyle oluşturulmalıdır. PendingIntent,PendingIntent.FLAG_ONE_SHOTişaretiyle oluşturulmamalıdır. Kullanıcı bir giriş seçip geri dönüp tekrar seçebilir. Bu durumdaPendingIntentiki kez tetiklenir.- Her girişin kendi
PendingIntentöğesine sahip olabilmesi içinPendingIntentöğeniz benzersiz bir istek koduyla oluşturulmalıdır.
Geçiş anahtarı oluşturma istekleri için giriş seçimini işleme
- Kullanıcı daha önce doldurulmuş bir
CreateEntryseçtiğinde ilgiliPendingIntentçağrılır ve ilişkili sağlayıcıActivityoluşturulur. - Etkinliğinizin
onCreateyöntemi çağrıldıktan sonra ilişkili intent'e erişin veProviderCreateCredentialRequestdeğerini almak için bunuPendingIntentHandersınıfına iletin. - İstekten
requestJson,callingAppInfoveclientDataHashdeğerlerini ayıklayın. - Intent ekinden yerel
accountIddeğerini çıkarın. Bu, örnek uygulamaya özel bir uygulamadır ve zorunlu değildir. Bu hesap kimliği, bu kimlik bilgisini bu hesap kimliğiyle depolamak için kullanılabilir. requestJsondoğ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çinPublicKeyCredentialCreationOptionsgibi 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.- Arama yerel bir Android uygulamasından geliyorsa arama uygulamasının asset-link'ini kontrol edin.
- Kimlik doğrulama istemi gösterilir. Aşağıdaki örnekte Android Biometric API kullanılmaktadır.
- Kimlik doğrulama başarılı olduğunda bir
credentialIdve anahtar çifti oluşturun. - Özel anahtarı yerel veritabanınıza
callingAppInfo.packageNameiçin kaydedin. - Ortak anahtar ve
credentialIdöğelerinden oluşan bir Web Authentication API JSON yanıtı oluşturun. Aşağıdaki örnekte, daha önce bahsedilen spesifikasyona göre JSON oluşturmaya yardımcı olanAuthenticatorAttestationResponseveFidoPublicKeyCredentialgibi 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. - Yukarıda oluşturulan JSON ile bir
CreatePublicKeyCredentialResponseoluşturun. PendingIntentHander.setCreateCredentialResponse()aracılığıylaCreatePublicKeyCredentialResponseöğesini birIntentöğesine ek olarak ayarlayın ve bu intent'i etkinliğin sonucuna ayarlayın.- 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.
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
// ...
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
)
}
}
@SuppressLint("RestrictedApi")
fun createPasskey(
requestJson: String,
callingAppInfo: CallingAppInfo?,
clientDataHash: ByteArray?,
accountId: String?
) {
val request = PublicKeyCredentialCreationOptions(requestJson)
val biometricPrompt = BiometricPrompt(
this,
{ }, // Pass in your own executor
object : AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
finish()
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
finish()
}
@RequiresApi(VERSION_CODES.P)
override fun onAuthenticationSucceeded(
result: 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,
authenticatorAttachment = "", // Add your authenticator attachment
)
val result = Intent()
val createPublicKeyCredResponse =
CreatePublicKeyCredentialResponse(credential.json())
// Set the CreateCredentialResponse as the result of the Activity
PendingIntentHandler.setCreateCredentialResponse(
result,
createPublicKeyCredResponse
)
setResult(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)
}
@RequiresApi(VERSION_CODES.P)
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şleme
Şifre oluşturma istekleriyle ilgili sorguları işlemek için aşağıdakileri yapın:
- Önceki bölümde bahsedilen
processCreateCredentialRequest()yönteminizin içinde, şifre isteklerini işlemek için switch bloğuna başka bir case ekleyin. BeginCreateCredentialResponseoluştururken gerekliCreateEntriesekleyin.- Her
CreateEntry, kimlik bilgilerinin kaydedilebileceği bir hesaba karşılık gelmeli ve diğer meta verilerle birlikte birPendingIntentayarlanmış 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
}
@RequiresApi(VERSION_CODES.M)
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 yönetme
Kullanıcı doldurulmuş bir CreateEntry seçtiğinde ilgili PendingIntent yürütülür ve ilişkili etkinlik açılır. onCreate içinde iletilen ilişkili intent'e erişin ve ProviderCreateCredentialRequest yöntemini almak için bunu 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)
if (createRequest == null) {
return
}
val request: CreatePasswordRequest = createRequest.callingRequest as CreatePasswordRequest
// Fetch the ID and password from the request and save it in your database
mDatabase.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)
finish()
Kullanıcı oturum açma işlemini yönetme
Kullanıcı oturum açma işlemi aşağıdaki adımlarla gerçekleştirilir:
- İstemci uygulaması bir kullanıcının oturumunu açmaya çalıştığında bir
GetCredentialRequestörneği hazırlar. - Android çerçevesi, bu hizmetlere bağlanarak bu isteği tüm geçerli kimlik bilgisi sağlayıcılara iletir.
- Sağlayıcı hizmeti daha sonra, eşleşen kimlik bilgilerini almak için kullanılabilecek parametreleri içeren
BeginGetCredentialOptionlistesini barındıran birBeginGetCredentialRequestalır.
Kimlik bilgisi sağlayıcı hizmetinizde bu isteği işlemek için aşağıdaki adımları tamamlayın:
İsteği işlemek için
onBeginGetCredentialRequest()yöntemini geçersiz kılın. Kimlik bilgileriniz kilitliyse yanıtta hemen birAuthenticationActionayarlayabilir ve geri çağırma işlevini çağırabilirsiniz.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
credentialEntriesdö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 beklemede olan bir intent ayarlamalı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 ) ) }Yerel veritabanınızdan kimlik bilgilerini alın ve seçicide gösterilecek şekilde
CredentialEntrieskullanarak ayarlayın. Geçiş anahtarları için intent'e ek olarakcredentialIddeğerini ayarlayabilirsiniz. Böylece, kullanıcı bu girişi seçtiğinde hangi kimlik bilgisiyle eşleştiğini öğrenebilirsiniz.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 processGetCredentialRequest( request: BeginGetCredentialRequest ): BeginGetCredentialResponse { val callingPackageInfo = request.callingAppInfo val callingPackageName = callingPackageInfo?.packageName.orEmpty() val credentialEntries: MutableList<CredentialEntry> = mutableListOf() for (option in request.beginGetCredentialOptions) { when (option) { is BeginGetPasswordOption -> { credentialEntries.addAll( populatePasswordData( callingPackageName, option ) ) } is BeginGetPublicKeyCredentialOption -> { credentialEntries.addAll( populatePasskeyData( callingPackageInfo, option ) ) } else -> { Log.i(TAG, "Request not supported") } } } return BeginGetCredentialResponse(credentialEntries) }Veritabanınızdan kimlik bilgilerini sorgulayın, doldurulacak 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 ), beginGetPublicKeyCredentialOption = 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) ) }Kimlik bilgilerini sorgulayıp doldurduktan sonra, kullanıcı tarafından seçilen kimlik bilgilerinin (geçiş anahtarı veya şifre) seçim aşamasını yönetmeniz gerekir.
Geçiş anahtarları için kullanıcı seçimini işleme
- İlgili etkinliğin
onCreateyönteminde ilişkili intent'i alın vePendingIntentHandler.retrieveProviderGetCredentialRequest()yöntemine iletin. - Yukarıda alınan istekten
GetPublicKeyCredentialOptiondeğerini ayıklayın. Ardından bu seçenektenrequestJsonveclientDataHash'i çıkarın. - İlgili
PendingIntentayarlandığında kimlik bilgisi sağlayıcı tarafından doldurulan intent ekindencredentialIddeğerini çıkarın. - Yukarıda erişilen istek parametrelerini kullanarak geçiş anahtarını yerel veritabanınızdan çıkarın.
Ayıklanan meta veriler ve kullanıcı doğrulaması ile geçiş anahtarının geçerli olduğunu onaylayın.
val getRequest = PendingIntentHandler.retrieveProviderGetCredentialRequest(intent) val publicKeyRequest = getRequest?.credentialOptions?.first() as GetPublicKeyCredentialOption val requestInfo = intent.getBundleExtra("CREDENTIAL_DATA") val credIdEnc = requestInfo?.getString("credId").orEmpty() // Get the saved passkey from your database based on the credential ID from the PublicKeyRequest val passkey = mDatabase.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 )Kullanıcıyı doğrulamak için biyometrik istem (veya başka bir onay yöntemi) gösterin. Aşağıdaki kod snippet'inde Android Biometric API kullanılmaktadır.
Kimlik doğrulama başarılı olduktan sonra W3 Web Authentication Assertion spesifikasyonuna göre bir JSON yanıtı oluşturun. Aşağıdaki kod snippet'inde, yapılandırılmış parametreleri alıp gerekli JSON biçimine dönüştürmek için
AuthenticatorAssertionResponsegibi yardımcı veri sınıfları kullanılmıştır. Yanıt, bir WebAuthn kimlik bilgisinin özel anahtarından alınmış bir dijital imza içerir. Güvenen tarafın sunucusu, oturum açmadan önce kullanıcının kimliğini doğrulamak için bu imzayı doğrulayabilir.Yukarıda oluşturulan JSON'u kullanarak bir
PublicKeyCredentialoluşturun ve bunu sonGetCredentialResponseüzerinde ayarlayın. Bu son yanıtı bu etkinliğin sonucuna göre belirleyin.
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,
{ }, // Pass in your own 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,
authenticatorAttachment = "", // Add your authenticator attachment
)
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)
Şifreyle kimlik doğrulama için kullanıcı seçimini işleme
- İlgili etkinliğinizde
onCreateöğesine iletilen intent'e erişin vePendingIntentHandlerkullanarakProviderGetCredentialRequestöğesini ayıklayın. Gelen paket adı için şifre kimlik bilgilerini almak üzere istekte
GetPasswordOptionkullanın.val getRequest = PendingIntentHandler.retrieveProviderGetCredentialRequest(intent) val passwordOption = getRequest?.credentialOptions?.first() as GetPasswordOption val username = passwordOption.allowedUserIds.first() // Fetch the credentials for the calling app package name val creds = mDatabase.getCredentials(callingAppInfo.packageName) val passwords = creds.passwords val it = passwords.iterator() var password = "" while (it.hasNext()) { val passwordItemCurrent = it.next() if (passwordItemCurrent.username == username) { password = passwordItemCurrent.password break } }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 bilgileri kilitliyse kimlik bilgisi sağlayıcı bir AuthenticationAction ayarlayabilir. 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 kimlik bilgilerinin kilidini açmak için biyometrik kimlik doğrulama akışı veya benzer bir mekanizma gösterebilir. İşlem başarılı olduğunda kimlik bilgileri sağlayıcı, kimlik bilgilerinin kilidi açıldığı için yukarıda açıklanan kullanıcı oturum açma işleminin işlenmesine benzer şekilde bir BeginGetCredentialResponse oluşturmalıdır. Ardından, hazırlanan intent sonuç olarak ayarlanmadan ve Activity tamamlanmadan önce bu yanıtın PendingIntentHandler.setBeginGetCredentialResponse() yöntemiyle ayarlanması gerekir.
Kimlik bilgisi isteklerini temizleme
İstemci uygulaması, kimlik bilgisi seçimi için tutulan tüm durumların temizlenmesini isteyebilir. Örneğin, kimlik bilgisi sağlayıcı daha önce seçilen kimlik bilgisini hatırlayabilir ve bir sonraki seferde yalnızca bu kimlik bilgisini döndürebilir. İstemci uygulaması bu API'yi çağırır ve yapışkan seçimin temizlenmesini bekler. Kimlik bilgisi sağlayıcı hizmetiniz, onClearCredentialStateRequest() yöntemini geçersiz kılarak bu isteği işleyebilir:
override fun onClearCredentialStateRequest(
request: ProviderClearCredentialStateRequest,
cancellationSignal: CancellationSignal,
callback: OutcomeReceiver<Void?, ClearCredentialException>
) {
// Delete any maintained state as appropriate.
}
Sağlayıcınızın ayarlar sayfasına bağlantı ekleme özelliği ekleyin.
Kullanıcılarınızın sağlayıcınızın ayarlarını Şifreler, geçiş anahtarları ve otomatik doldurma ekranından açmasına izin vermek için kimlik bilgisi sağlayıcı uygulamalarının res/xml/provider.xml içinde credential-provider settingsActivity manifest özelliğini uygulaması gerekir. 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ı olarak ayarlayın.
<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>
Ayarlar amaçları
Ayarları açma: android.settings.CREDENTIAL_PROVIDER intent'i, kullanıcının tercih ettiği ve ek kimlik bilgisi sağlayıcılarını seçebileceği bir ayarlar ekranı açar.
Tercih edilen kimlik bilgisi hizmeti: ACTION_REQUEST_SET_AUTOFILL_SERVICE intent'i, kullanıcınızı tercih edilen sağlayıcı seçim ekranına yönlendirir. Bu ekranda seçilen sağlayıcı, tercih edilen kimlik bilgileri ve otomatik doldurma sağlayıcısı olur.
Ayrıcalıklı uygulamaların izin verilenler listesini alma
Web tarayıcıları gibi ayrıcalıklı uygulamalar, Kimlik Bilgisi Yöneticisi'nin 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ı, bu istekleri işlemek için getOrigin() API'sini kullanarak origin değerini alır.
origin değerini almak için kimlik bilgisi sağlayıcı uygulamasının, androidx.credentials.provider.CallingAppInfo's getOrigin() API'sine ayrıcalıklı ve güvenilir arayanların listesini iletmesi 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 iletilen privilegedAllowlist içinde bulunan bir uygulamanınkilerle eşleşirse origin döndürülür. origin değeri alındıktan sonra sağlayıcı uygulama, bunu ayrıcalıklı bir çağrı olarak kabul etmeli ve arayan uygulamanın imzasını kullanarak origin değerini hesaplamak yerine AuthenticatorResponse içindeki origin değerini istemci verilerinde ayarlamalıdır.
origin alırsanız imza isteği sırasında clientDataJSON oluşturup karma oluşturmak yerine doğrudan CreatePublicKeyCredentialRequest() veya GetPublicKeyCredentialOption() içinde sağlanan clientDataHash kullanın. JSON ayrıştırma sorunlarını önlemek için onay ve iddia 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ı seçmek sağlayıcıya bağlıdır. Üçüncü taraf kimlik bilgisi sağlayıcılarla 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ın, sağlayıcıyı Cihaz ayarları > Şifreler ve Hesaplar > Sağlayıcınız > Etkinleştir veya Devre Dışı Bırak bölümünden etkinleştirmesi gerekir.
fun createSettingsPendingIntent(): PendingIntent