„Anmeldedaten-Manager“ bezieht sich auf eine Reihe von APIs, die in Android 14 eingeführt wurden und mehrere Anmeldemethoden unterstützen, z. B. Nutzername und Passwort, Passkeys und Lösungen für die föderierte Anmeldung (z. B. „Über Google anmelden“). Wenn die Credential Manager API aufgerufen wird, aggregiert das Android-System Anmeldedaten von allen auf dem Gerät installierten Anmeldedatenanbietern. In diesem Dokument werden die APIs beschrieben, die Integrationsendpunkte für diese Anmeldedatenanbieter bereitstellen.
Einrichtung
Bevor Sie Funktionen in Ihrem Anmeldedatenanbieter implementieren, führen Sie die Einrichtungsschritte in den folgenden Abschnitten aus.
Abhängigkeiten deklarieren
Fügen Sie dem Build-Skript Ihres App-Moduls die folgenden Abhängigkeiten hinzu, um die neueste Version der Credential Manager-Bibliothek zu verwenden:
Kotlin
dependencies { implementation("androidx.credentials:credentials:1.7.0-alpha02") }
Groovy
dependencies { implementation "androidx.credentials:credentials:1.7.0-alpha02" }
Dienstelement in der Manifestdatei deklarieren
Fügen Sie in der Manifestdatei Ihrer App AndroidManifest.xml eine <service>
Deklaration für eine Dienstklasse ein, die die Klasse
CredentialProviderService aus der androidx.credentials-Bibliothek erweitert, wie
im folgenden Beispiel gezeigt.
<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>
Die Berechtigung und der Intent-Filter im vorherigen Beispiel sind unerlässlich, damit der Credential Manager wie erwartet funktioniert. Die Berechtigung ist erforderlich, damit nur das Android-System eine Bindung an diesen Dienst vornehmen kann. Der Intent-Filter wird verwendet, damit dieser Dienst als Anmeldedatenanbieter vom Anmeldedaten-Manager erkannt werden kann.
Unterstützte Anmeldedatentypen deklarieren
Erstellen Sie im Verzeichnis res/xml eine neue Datei mit dem Namen provider.xml. Deklarieren Sie in dieser Datei die Anmeldedatentypen, die Ihr Dienst unterstützt, über Konstanten, die für jeden Anmeldedatentyp in der Bibliothek definiert sind. Im folgenden Beispiel unterstützt der
Dienst sowohl herkömmliche Passwörter als auch Passkeys. Die Konstanten dafür
sind TYPE_PASSWORD_CREDENTIAL und
TYPE_PUBLIC_KEY_CREDENTIAL:
<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>
Auf früheren API-Ebenen werden Anmeldedatenanbieter in APIs wie Autofill für Passwörter und andere Daten eingebunden. Diese Anbieter können dieselbe interne Infrastruktur verwenden, um die vorhandenen Anmeldedatentypen zu speichern, und sie gleichzeitig erweitern, um andere zu unterstützen, einschließlich Passkeys.
Zweiphasiger Ansatz für die Interaktion mit Anbietern
Der Credential Manager interagiert in zwei Phasen mit Anmeldedatenanbietern:
- Die erste Phase ist die Beginn-/Abfragephase, in der das System eine Bindung an
Anmeldedatenanbieterdienste herstellt und die Methoden
onBeginGetCredentialRequest(),onBeginCreateCredentialRequest()oderonClearCredentialStateRequest()mitBegin…-Anfragen aufruft. Anbieter müssen diese Anfragen verarbeiten und mitBegin…-Antworten reagieren, die Einträge enthalten, die visuelle Optionen darstellen, die in der Kontoauswahl angezeigt werden sollen. Für jeden Eintrag muss einPendingIntentfestgelegt sein. - Sobald der Nutzer einen Eintrag auswählt, beginnt die Auswahlphase und das
PendingIntentmit dem Eintrag verknüpfte wird ausgelöst, wodurch die entsprechende Anbieteraktivität aufgerufen wird. Nachdem der Nutzer die Interaktion mit dieser Aktivität beendet hat, muss der Anmeldedatenanbieter die Antwort auf das Ergebnis der Aktivität festlegen, bevor er sie beendet. Diese Antwort wird dann an die Clientanwendung gesendet, die den Anmeldedaten-Manager aufgerufen hat.
Erstellung von Passkeys verarbeiten
Abfragen zur Erstellung von Passkeys verarbeiten
Wenn eine Clientanwendung einen Passkey erstellen und bei einem
Anmeldedatenanbieter speichern möchte, ruft sie die createCredential-API auf. Führen Sie die Schritte in den folgenden Abschnitten aus, um diese Anfrage in Ihrem Anmeldedatenanbieterdienst so zu verarbeiten, dass der Passkey tatsächlich in Ihrem Speicher gespeichert wird.
- Überschreiben Sie die
onBeginCreateCredentialRequest()Methode in Ihrem Dienst der vonCredentialProviderServiceerweitert wurde. - Verarbeiten Sie die
BeginCreateCredentialRequest, indem Sie eine entsprechendeBeginCreateCredentialResponseerstellen und sie über den Callback übergeben. - Fügen Sie beim Erstellen der
BeginCreateCredentialResponsedie erforderlichenCreateEntrieshinzu. JederCreateEntrysollte einem Konto entsprechen, in dem die Anmeldedaten gespeichert werden können, und muss neben anderen erforderlichen Metadaten einPendingIntententhalten.
Das folgende Beispiel zeigt, wie Sie diese Schritte implementieren.
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
)
)
}
Die Erstellung von PendingIntent muss die folgenden Anforderungen erfüllen:
- Die entsprechende Aktivität muss so eingerichtet sein, dass alle erforderlichen biometrischen Aufforderungen, Bestätigungen oder Auswahlen angezeigt werden.
- Alle erforderlichen Daten, die der Anbieter benötigt, wenn die entsprechende Aktivität aufgerufen wird, müssen als Extra für den Intent festgelegt werden, der zum Erstellen von
PendingIntentverwendet wird, z. B. eineaccountIdim Erstellungsprozess. - Ihr
PendingIntentmuss mit dem FlagPendingIntent.FLAG_MUTABLEerstellt werden, damit das System die endgültige Anfrage an das Intent-Extra anhängen kann. - Ihr
PendingIntentdarf nicht mit dem FlagPendingIntent.FLAG_ONE_SHOTerstellt werden, da der Nutzer einen Eintrag auswählen, zurückgehen und ihn noch einmal auswählen kann, wodurch dasPendingIntentzweimal ausgelöst würde. - Ihr
PendingIntentmuss mit einem eindeutigen Anfragecode erstellt werden, damit jeder Eintrag ein eigenes entsprechendesPendingIntenthaben kann.
Eintrag für Anfragen zur Erstellung von Passkeys auswählen
- Wenn der Nutzer einen zuvor ausgefüllten
CreateEntryauswählt, wird das entsprechendePendingIntentaufgerufen und die zugehörige Anbieter-Activityerstellt. - Nachdem die
onCreateMethode Ihrer Aktivität aufgerufen wurde, greifen Sie auf den zugehörigen Intent zu und übergeben Sie ihn an diePendingIntentHanderKlasse, um dieProviderCreateCredentialRequestzu erhalten. - Extrahieren Sie
requestJson,callingAppInfoundclientDataHashaus der Anfrage. - Extrahieren Sie die lokale
accountIdaus dem Intent-Extra. Dies ist eine spezifische Implementierung für die Beispiel-App und ist nicht erforderlich. Diese Konto-ID kann verwendet werden, um diese Anmeldedaten für diese bestimmte Konto-ID zu speichern. - Validieren Sie
requestJson. Im folgenden Beispiel werden lokale Datenklassen wiePublicKeyCredentialCreationOptionsverwendet, um die Eingabe-JSON gemäß der WebAuthn-Spezifikation in eine strukturierte Klasse zu konvertieren. Als Anmeldedatenanbieter können Sie dies durch Ihren eigenen Parser ersetzen. - Prüfen Sie den Asset-Link für die aufrufende App, wenn der Aufruf von einer nativen Android-App stammt.
- Zeigen Sie eine Authentifizierungsaufforderung an. Im folgenden Beispiel wird die Android Biometric API verwendet.
- Wenn die Authentifizierung erfolgreich ist, generieren Sie eine
credentialIdund ein Schlüssel paar. - Speichern Sie den privaten Schlüssel in Ihrer lokalen Datenbank unter
callingAppInfo.packageName. - Erstellen Sie eine JSON-Antwort der Web Authentication API, die
aus dem öffentlichen Schlüssel und der
credentialIdbesteht. Im folgenden Beispiel werden lokale Hilfsklassen wieAuthenticatorAttestationResponseundFidoPublicKeyCredentialverwendet, um eine JSON-Datei basierend auf der oben genannten Spezifikation zu erstellen.Als Anmeldedatenanbieter können Sie diese Klassen durch Ihre eigenen Builder ersetzen. - Erstellen Sie eine
CreatePublicKeyCredentialResponsemit der oben generierten JSON-Datei. - Legen Sie
CreatePublicKeyCredentialResponseals Extra für einenIntentüberPendingIntentHander.setCreateCredentialResponse()fest und legen Sie diesen Intent als Ergebnis der Aktivität fest. - Beenden Sie die Aktivität.
Das folgende Codebeispiel veranschaulicht diese Schritte. Dieser Code muss in Ihrer Aktivitätsklasse verarbeitet werden, sobald onCreate() aufgerufen wird.
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)}"
}
Abfragen für Anfragen zur Erstellung von Passwörtern verarbeiten
So verarbeiten Sie Abfragen für Anfragen zur Erstellung von Passwörtern:
- Fügen Sie in der Methode
processCreateCredentialRequest(), die im vorherigen Abschnitt erwähnt wurde, einen weiteren Fall im Switch-Block hinzu, um Passwortanfragen zu verarbeiten. - Fügen Sie beim Erstellen der
BeginCreateCredentialResponsedie erforderlichenCreateEntrieshinzu. - Jeder
CreateEntrysollte einem Konto entsprechen, in dem die Anmeldedaten gespeichert werden können, und muss neben anderen Metadaten einPendingIntententhalten.
Das folgende Beispiel zeigt, wie Sie diese Schritte implementieren:
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)
}
Eintrag für Anfragen zur Erstellung von Passwörtern auswählen
Wenn der Nutzer einen ausgefüllten CreateEntry auswählt, wird das entsprechende PendingIntent ausgeführt und die zugehörige Aktivität aufgerufen. Greifen Sie auf den zugehörigen Intent zu, der in onCreate übergeben wurde, und übergeben Sie ihn an die Klasse PendingIntentHander, um die Methode ProviderCreateCredentialRequest zu erhalten.
Das folgende Beispiel zeigt, wie Sie diesen Prozess implementieren. Dieser Code muss in der Methode onCreate() Ihrer Aktivität verarbeitet werden.
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()
Nutzeranmeldung verarbeiten
Die Nutzeranmeldung wird mit den folgenden Schritten verarbeitet:
- Wenn eine Clientanwendung versucht, einen Nutzer anzumelden, wird eine
GetCredentialRequest-Instanz vorbereitet. - Das Android-Framework leitet diese Anfrage an alle entsprechenden Anmeldedatenanbieter weiter, indem es eine Bindung an diese Dienste herstellt.
- Der Anbieterdienst erhält dann eine
BeginGetCredentialRequest, die eine Liste vonBeginGetCredentialOptionenthält. Jede Option enthält Parameter, mit denen übereinstimmende Anmeldedaten abgerufen werden können.
Führen Sie die folgenden Schritte aus, um diese Anfrage in Ihrem Anmeldedatenanbieterdienst zu verarbeiten:
Überschreiben Sie die Methode
onBeginGetCredentialRequest(), um die Anfrage zu verarbeiten. Wenn Ihre Anmeldedaten gesperrt sind, können Sie sofort eineAuthenticationActionfür die Antwort festlegen und den Callback aufrufen.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()) } }Anbieter, bei denen die Anmeldedaten entsperrt werden müssen, bevor
credentialEntrieszurückgegeben werden, müssen ein PendingIntent einrichten, das den Nutzer zum Entsperrprozess der App weiterleitet: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 ) ) }Rufen Sie Anmeldedaten aus Ihrer lokalen Datenbank ab und richten Sie sie mit
CredentialEntriesein, damit sie in der Auswahl angezeigt werden. Bei Passkeys können SiecredentialIdals Extra für den Intent festlegen, damit Sie wissen, welchen Anmeldedaten sie zugeordnet ist, wenn der Nutzer diesen Eintrag auswählt.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) }Fragen Sie Anmeldedaten aus Ihrer Datenbank ab und erstellen Sie Passkey- und Passworteinträge, um sie auszufüllen.
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) ) }Nachdem Sie die Anmeldedaten abgefragt und ausgefüllt haben, müssen Sie nun die Auswahlphase für die Anmeldedaten verarbeiten, die vom Nutzer ausgewählt wurden, unabhängig davon, ob es sich um einen Passkey oder ein Passwort handelt.
Nutzerauswahl für Passkeys verarbeiten
- Rufen Sie in der Methode
onCreateder entsprechenden Aktivität den zugehörigen Intent ab und übergeben Sie ihn anPendingIntentHandler.retrieveProviderGetCredentialRequest(). - Extrahieren Sie
GetPublicKeyCredentialOptionaus der oben abgerufenen Anfrage. Extrahieren Sie anschließendrequestJsonundclientDataHashaus dieser Option. - Extrahieren Sie die
credentialIdaus dem Intent-Extra, das vom Anmeldedatenanbieter ausgefüllt wurde, als das entsprechendePendingIntenteingerichtet wurde. - Extrahieren Sie den Passkey aus Ihrer lokalen Datenbank mit den oben abgerufenen Anfrageparametern.
Bestätigen Sie, dass der Passkey mit den extrahierten Metadaten und der Nutzerbestätigung gültig ist.
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 )Um den Nutzer zu validieren, zeigen Sie eine biometrische Aufforderung (oder eine andere Bestätigungsmethode) an. Im folgenden Code-Snippet wird die Android Biometric API verwendet.
Nachdem die Authentifizierung erfolgreich war, erstellen Sie eine JSON-Antwort basierend auf der W3 Web Authentication Assertion-Spezifikation. Im folgenden Code-Snippet werden Hilfsdatenklassen wie
AuthenticatorAssertionResponseverwendet, um strukturierte Parameter zu übernehmen und in das erforderliche JSON-Format zu konvertieren. Die Antwort enthält eine digitale Signatur vom privaten Schlüssel eines WebAuthn-Berechtigungsnachweises. Der Server der Relying Party kann diese Signatur überprüfen, um einen Nutzer vor der Anmeldung zu authentifizieren.Erstellen Sie mit der oben generierten JSON-Datei eine
PublicKeyCredentialund legen Sie sie für eine endgültigeGetCredentialResponsefest. Legen Sie diese endgültige Antwort als Ergebnis dieser Aktivität fest.
Das folgende Beispiel zeigt, wie diese Schritte implementiert werden können:
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)
Nutzerauswahl für die Passwortauthentifizierung verarbeiten
- Greifen Sie in der entsprechenden Aktivität auf den Intent zu, der an
onCreateübergeben wurde, und extrahieren Sie dieProviderGetCredentialRequestmitPendingIntentHandler. Verwenden Sie
GetPasswordOptionin der Anfrage, um Passwort anmeldedaten für den eingehenden Paketnamen abzurufen.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 } }Legen Sie nach dem Abrufen die Antwort für die ausgewählten Passwortanmeldedaten fest.
// Set the response back val result = Intent() val passwordCredential = PasswordCredential(username, password) PendingIntentHandler.setGetCredentialResponse( result, GetCredentialResponse(passwordCredential) ) setResult(Activity.RESULT_OK, result) finish()
Auswahl eines Eintrags für eine Authentifizierungsaktion verarbeiten
Wie bereits erwähnt, kann ein Anmeldedatenanbieter ein
AuthenticationAction festlegen, wenn die Anmeldedaten gesperrt sind. Wenn der Nutzer diesen Eintrag auswählt, wird die Aktivität aufgerufen, die der in PendingIntent festgelegten Intent-Aktion entspricht. Anmeldedatenanbieter können dann einen biometrischen Authentifizierungsprozess oder einen ähnlichen Mechanismus verwenden, um die Anmeldedaten zu entsperren. Wenn dies erfolgreich ist,
muss der Anmeldedatenanbieter eine BeginGetCredentialResponse erstellen, ähnlich
wie oben beschrieben, da die Anmeldedaten jetzt
entsperrt sind. Diese Antwort muss dann über die
PendingIntentHandler.setBeginGetCredentialResponse() Methode festgelegt werden, bevor der
vorbereitete Intent als Ergebnis festgelegt und die Aktivität beendet wird.
Anfragen zum Löschen von Anmeldedaten
Eine Clientanwendung kann anfordern, dass alle für die Auswahl von Anmeldedaten gespeicherten Zustände gelöscht werden. Ein Anmeldedatenanbieter kann sich beispielsweise die zuvor ausgewählten Anmeldedaten merken und sie beim nächsten Mal zurückgeben. Eine Clientanwendung ruft diese API auf und erwartet, dass die Auswahl gelöscht wird. Ihr Anmeldedatenanbieterdienst
kann diese Anfrage verarbeiten, indem er die
onClearCredentialStateRequest() Methode überschreibt:
override fun onClearCredentialStateRequest(
request: ProviderClearCredentialStateRequest,
cancellationSignal: CancellationSignal,
callback: OutcomeReceiver<Void?, ClearCredentialException>
) {
// Delete any maintained state as appropriate.
}
Funktion zum Verknüpfen mit der Einstellungsseite Ihres Anbieters hinzufügen
Damit Nutzer die Einstellungen Ihres Anbieters über den Bildschirm Passwörter, Passkeys und Autofill öffnen können, sollten Anmeldedatenanbieter-Apps das Manifestattribut credential-provider settingsActivity in res/xml/provider.xml implementieren. Mit diesem Attribut können Sie einen Intent verwenden, um den Einstellungsbildschirm Ihrer App zu öffnen, wenn ein Nutzer in der Liste der Dienste Passwörter, Passkeys und Autofill auf einen Anbieternamen klickt. Legen Sie den Wert dieses Attributs auf den Namen der Aktivität fest, die über den Einstellungsbildschirm gestartet werden soll.
<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>
Einstellungen für Intents
Einstellungen öffnen: Der android.settings.CREDENTIAL_PROVIDER Intent
ruft einen Einstellungsbildschirm auf, auf dem der Nutzer seine bevorzugten und
zusätzlichen Anmeldedatenanbieter auswählen kann.
Bevorzugter Anmeldedatendienst: Der
ACTION_REQUEST_SET_AUTOFILL_SERVICE Intent leitet den Nutzer zum
Bildschirm für die Auswahl des bevorzugten Anbieters weiter. Der auf diesem Bildschirm ausgewählte Anbieter wird zum bevorzugten Anbieter für Anmeldedaten und Autofill.
Zulassungsliste privilegierter Apps abrufen
Privilegierte Apps wie Webbrowser rufen den Credential Manager im Namen anderer Relying Parties auf, indem sie den origin Parameter in den Credential Manager GetCredentialRequest() und CreatePublicKeyCredentialRequest() Methoden festlegen. Um diese Anfragen zu verarbeiten,
ruft der Anmeldedatenanbieter den origin mit der getOrigin()
API ab.
Um den origin abzurufen, muss die Anmeldedatenanbieter-App eine Liste
privilegierter und vertrauenswürdiger Aufrufer an die
androidx.credentials.provider.CallingAppInfo's getOrigin() API übergeben. Diese Zulassungsliste muss ein gültiges JSON-Objekt sein. Der origin wird zurückgegeben, wenn der packageName und die Zertifikatsfingerabdrücke, die von signingInfo abgerufen wurden, mit denen einer App übereinstimmen, die in der privilegedAllowlist enthalten ist, die an die API getOrigin() übergeben wurde. Nachdem der
origin Wert abgerufen wurde, sollte die Anbieter-App dies als privilegierten
Aufruf betrachten und diesen origin in den Clientdaten
in der AuthenticatorResponse festlegen, anstatt den
origin mit der Signatur der aufrufenden App zu berechnen.
Wenn Sie ein origin abrufen, verwenden Sie das clientDataHash, das direkt
in CreatePublicKeyCredentialRequest() oder
GetPublicKeyCredentialOption() angegeben ist, anstatt clientDataJSON während der Signaturanfrage zusammenzustellen und zu hashen. Um Probleme beim Parsen von JSON zu vermeiden, legen Sie in der Attestierungs- und Bestätigungsantwort einen Platzhalterwert für clientDataJSON fest. Der Google Passwortmanager verwendet eine öffentlich verfügbare
Zulassungsliste für Aufrufe von getOrigin(). Als Anmeldedatenanbieter können Sie diese Liste verwenden oder Ihre eigene im von der API beschriebenen JSON-Format bereitstellen. Es liegt in der Verantwortung des Anbieters, welche Liste verwendet wird. Informationen zum privilegierten Zugriff mit Anmeldedatenanbietern von Drittanbietern finden Sie in der Dokumentation des Drittanbieters.
Anbieter auf einem Gerät aktivieren
Nutzer müssen den Anbieter über Geräteeinstellungen > Passwörter und Konten > Ihr Anbieter > Aktivieren oder Deaktivieren aktivieren.
fun createSettingsPendingIntent(): PendingIntent