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ı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ı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ı bildirme
Modülünüzün build.gradle
dosyasında, Kimlik Bilgisi Yöneticisi kitaplığının son sürümünü kullanarak bir bağımlılık beyan edin:
implementation "androidx.credentials:credentials:1.2.0-{latest}"
Manifest dosyasında hizmet öğesini tanımlama
Uygulamanızın manifest dosyasına AndroidManifest.xml
, aşağıdaki örnekte gösterildiği gibi androidx.credentials kitaplığındaki CredentialProviderService
sınıfını 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 gereklidir. Bu izin, yalnızca Android sisteminin bu hizmete bağlanabilmesi için gereklidir. 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. Aşağıdaki örnekte, hizmet hem geleneksel şifreleri hem de geçiş anahtarlarını desteklemektedir. Geçiş anahtarlarının sabitleri TYPE_PASSWORD_CREDENTIAL
ve TYPE_PUBLIC_KEY_CREDENTIAL
olarak tanımlanmıştır:
<?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şimine 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 doldurmalıdır. Her giriştePendingIntent
ayarlanmış olmalıdır. - Kullanıcı bir giriş seçtikten sonra seçim aşaması başlar ve girişle ilişkili
PendingIntent
tetiklenerek ilgili sağlayıcı etkinliği açılır. Kullanıcı bu etkinlikle etkileşimi tamamladığında kimlik bilgisi sağlayıcı, etkinliği sonlandırmadan önce etkinliğin sonucuna yanıtı ayarlamalıdır. Bu yanıt, kimlik bilgisi yöneticisini ç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
Bir istemci uygulaması geçiş anahtarı oluşturmak ve bunu bir kimlik bilgisi sağlayıcıda depolamak istediğinde createCredential
API'yi çağırır. Bu isteği, kimlik bilgisi sağlayıcı hizmetinizde geçiş anahtarının depolama alanınızda depolanacağı şekilde işlemek için aşağıdaki bölümlerde gösterilen adımları tamamlayın.
CredentialProviderService
'den genişletilen hizmetinizdeonBeginCreateCredentialRequest()
yöntemini geçersiz kılın.- İlgili bir
BeginCreateCredentialResponse
oluşturup geri çağırma işleviyle ileterekBeginCreateCredentialRequest
'i işleyin. BeginCreateCredentialResponse
oluştururken gerekliCreateEntries
öğesini ekleyin. HerCreateEntry
, kimlik bilgisinin kaydedilebileceği bir hesaba karşılık gelmelidir ve gerekli diğer meta verilerle birlikte birPendingIntent
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 tüm biyometri istemlerini, onaylarını veya seçimlerini gösterecek şekilde ayarlanmalıdır.
- İlgili etkinlik çağrıldığında sağlayıcının ihtiyaç duyduğu tüm zorunlu veriler,
PendingIntent
'inizi oluşturmak için kullanılan intent'te ek olarak ayarlanmalıdır (ör. oluşturma akışında biraccountId
). - Sistemin, son isteği ekstra amaca ekleyebilmesi için
PendingIntent
öğenizinPendingIntent.FLAG_MUTABLE
işaretiyle yapılandırılması gerekir. - Kullanıcı bir giriş seçip geri dönüp yeniden seçebileceği için
PendingIntent
öğenizPendingIntent.FLAG_ONE_SHOT
işaretiyle oluşturulmamalıdır. Bu durum,PendingIntent
öğesinin iki kez tetiklenmesine neden olur. - Her girişin karşılık gelen
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
CreateEntry
seçtiğinde ilgiliPendingIntent
çağrılır ve ilişkili sağlayıcıActivity
oluşturulur. - Aktivitenizin
onCreate
yöntemi çağrıldıktan sonra, ilişkili intent'e erişin veProviderCreateCredentialRequest
almak içinPendingIntentHander
sınıfına aktarın. - İstekten
requestJson
,callingAppInfo
veclientDataHash
öğelerini ayıklayın. - Ekstra intent'den yerel
accountId
öğesini çıkarın. Bu, uygulamaya özel bir örnek uygulamadır ve gerekli değildir. Bu hesap kimliği, kimlik bilgisini bu hesap kimliğine göre depolamak için kullanılabilir. requestJson
öğesini doğrulayın. Aşağıdaki örnekte, giriş JSON dosyasını WebAuthn spesifikasyonuna göre yapılandırılmış bir sınıfa dönüştürmek içinPublicKeyCredentialCreationOptions
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.- Arama, yerel bir Android uygulamasından geliyorsa arayan uygulamanın asset-link değerini kontrol edin.
- Kimlik doğrulama istemi gösterin. Aşağıdaki örnekte Android Biometric API'si kullanılmaktadır.
- Kimlik doğrulama başarılı olduğunda bir
credentialId
ve bir anahtar çifti oluşturun. - Özel anahtarı yerel veritabanınızda
callingAppInfo.packageName
ile ilişkili olarak kaydedin. - Ortak anahtar ve
credentialId
içeren bir Web Authentication API JSON yanıtı oluşturun. Aşağıdaki örnekte, daha önce bahsedilen spesifikasyona göre bir JSON oluşturulmasına yardımcı olanAuthenticatorAttestationResponse
veFidoPublicKeyCredential
gibi yerel yardımcı program sınıfları kullanılmaktadır.Kimlik bilgisi sağlayıcısı olarak bu sınıfları kendi derleyicilerinizle değiştirebilirsiniz. - Yukarıda oluşturulan JSON ile bir
CreatePublicKeyCredentialResponse
oluşturun. PendingIntentHander.setCreateCredentialResponse()
aracılığıylaCreatePublicKeyCredentialResponse
'ü birIntent
'te ekstra olarak ayarlayın ve bu intent'i Etkinlik'in sonucu olarak ayarlayın.- Etkinliği tamamlayın.
Aşağıdaki kod örneğinde bu adımlar gösterilmektedir. Bu kodun, onCreate()
çağrıldıktan sonra Activity 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 istekleri için 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çine, şifre isteklerini işlemek için switch bloğuna başka bir durum ekleyin. BeginCreateCredentialResponse
oluştururken gerekliCreateEntries
ekleyin.- Her
CreateEntry
, kimlik bilgisinin kaydedilebileceği bir hesaba karşılık gelmeli ve üzerinde diğer meta verilerle birlikte birPendingIntent
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, ilgili PendingIntent
yürütülüp ilişkili Etkinliği açar. 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 bir
GetCredentialRequest
örneği hazırlar. - Android çerçevesi, bu isteği bu hizmetlere bağlayarak geçerli tüm kimlik bilgisi sağlayıcılarına iletir.
- Ardından sağlayıcı hizmeti, her biri eşleşen kimlik bilgilerini almak için kullanılabilecek parametreler içeren
BeginGetCredentialOption
listesini içeren birBeginGetCredentialRequest
alır.
Bu isteği kimlik bilgisi sağlayıcı hizmetinizde 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 birAuthenticationAction
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 ) ) }
Kimlik bilgilerini yerel veritabanınızdan alın ve seçicide gösterilmesi için
CredentialEntries
kullanarak ayarlayın. Geçiş anahtarları içincredentialId
değerini ekstra olarak ayarlayarak kullanıcı bu girişi seçtiğinde hangi kimlik bilgileriyle eşlendiğ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 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) }
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) ) }
Kimlik bilgilerini sorgulayıp doldurduktan sonra artık kullanıcı tarafından seçilen kimlik bilgileri için (geçiş anahtarı veya şifre) seçim aşamasını gerçekleştirmeniz gerekir.
Geçiş anahtarları için kullanıcı seçimini işleme
- İlgili Etkinlik'in
onCreate
yönteminde ilişkili intent'i alın vePendingIntentHandler.retrieveProviderGetCredentialRequest()
'ye iletin. - Yukarıda alınan istekten
GetPublicKeyCredentialOption
dosyasını çıkarın. Ardından, bu seçenektenrequestJson
veclientDataHash
dosyalarını çıkarın. - İlgili
PendingIntent
ayarlandığında kimlik bilgisi sağlayıcı tarafından doldurulan intent ekstrasındancredentialId
öğesini çıkarın. - Yukarıda belirtilen istek parametrelerini kullanarak geçiş anahtarını yerel veritabanınızdan çıkarın.
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 )
Kullanıcıyı doğrulamak için bir biyometrik istem (veya başka bir iddia yöntemi) gösterin. Aşağıdaki kod snippet'i Android Biometrik API'yi kullanır.
Kimlik doğrulama başarılı olduğunda W3 Web Kimlik Doğrulama Beyanıyla İlgili Spesifikasyon'a dayalı 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
AuthenticatorAssertionResponse
gibi yardımcı veri sınıfları kullanılır. Yanıt, WebAuthn kimlik bilgisinin özel anahtarından alınan 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
PublicKeyCredential
oluşturun ve nihaiGetCredentialResponse
'e ayarlayın. Bu etkinliğin sonucuna göre nihai 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
- İlgili etkinliğinizde,
onCreate
hizmetine iletilen amaca erişin vePendingIntentHandler
öğesini kullanarakProviderGetCredentialRequest
öğesini çıkarın. Gelen paket adının şifre kimlik bilgilerini almak için 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 } }
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şi seçimini işleyin
Daha önce de belirtildiği gibi, kimlik bilgileri kilitliyse kimlik bilgisi sağlayıcı
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. Daha sonra kimlik bilgisi sağlayıcılar, 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 bilgilerinin kilidi açılmış olacağından kimlik bilgisi sağlayıcı, kullanıcı oturum açma işleminin yukarıda açıklandığı şekilde bir BeginGetCredentialResponse
oluşturmalıdır. Daha sonra bu yanıt, hazırlanan amaç sonuç olarak ayarlanmadan ve Etkinlik tamamlanmadan önce PendingIntentHandler.setBeginGetCredentialResponse()
yöntemiyle ayarlanmalıdır.
Kimlik bilgisi isteklerini temizleme
Bir istemci uygulaması, kimlik bilgisi seçimi için korunan herhangi bir durumun temizlenmesini isteyebilir. Örneğin, kimlik bilgisi sağlayıcı daha önce seçilen kimlik bilgilerini hatırlayıp bir sonraki sefer iade edebilir. İ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: android.service.credentials.ClearCredentialStateRequest,
cancellationSignal: CancellationSignal,
callback: OutcomeReceiver<Void?, ClearCredentialException>,
) {
// Delete any maintained state as appropriate.
}
Sağlayıcınızın ayarlar sayfasına bağlantı oluşturma özelliği ekleyin
Kullanıcılarınızın sağlayıcı ayarlarınızı Şifreler, geçiş anahtarları ve otomatik doldurma ekranından açmasına izin vermek için kimlik bilgisi sağlayıcı uygulamaları, res/xml/provider.xml
ürününde 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ına 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>
Amaçları ayarla
Ayarları açma: android.settings.CREDENTIAL_PROVIDER
niyeti, 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 GetCredentialRequest()
ve CreatePublicKeyCredentialRequest()
yöntemlerinde origin
parametresini ayarlayarak diğer güvenen taraflar adına Kimlik Bilgisi Yöneticisi çağrıları yapar. Kimlik bilgisi sağlayıcı, bu istekleri işlemek için getOrigin()
API'yi kullanarak origin
öğesini alır.
Kimlik bilgisi sağlayıcı uygulamasının, origin
öğesini almak için ayrıcalıklı ve güvenilir arayanların bir listesini androidx.credentials.provider.CallingAppInfo's getOrigin()
API'ye iletmesi gerekir. 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 elde edildikten sonra sağlayıcı uygulaması, bu çağrıyı ayrıcalıklı bir çağrı olarak değerlendirmeli ve origin
değerini arayan uygulamanın imzasını kullanarak hesaplamak yerine AuthenticatorResponse
içinde müşteri verilerinde ayarlamalıdır.origin
Bir origin
alırsanız imza isteği sırasında derlemek ve karma oluşturma işlemi uygulamak yerine
doğrudan CreatePublicKeyCredentialRequest()
veya GetPublicKeyCredentialOption()
içinde sağlanan clientDataHash
özelliğini kullanın.clientDataJSON
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ılarıyla ayrıcalıklı erişim elde etmek için üçüncü tarafın sağladığı belgelere bakın.
Bir cihazda sağlayıcıları etkinleştirme
Kullanıcıların cihaz ayarları > Şifreler ve Hesaplar > Sağlayıcınız > Etkinleştir veya Devre Dışı Bırak'ı kullanarak sağlayıcıyı etkinleştirmesi gerekir.
fun createSettingsPendingIntent(): PendingIntent