Der Anmeldedaten-Manager bezieht sich auf eine Reihe von APIs, die in Android 14 eingeführt wurden und mehrere Anmeldemethoden wie Nutzername und Passwort, Passkeys und Lösungen für die föderierte Anmeldung (z. B. „Über Google anmelden“) unterstützen. Wenn die Credential Manager API aufgerufen wird, fasst das Android-System Anmeldedaten aus allen Anmeldedaten zusammen. die auf dem Gerät installiert sind. In diesem Dokument werden die APIs beschrieben, Integrationsendpunkte für diese Anmeldeinformationsanbieter bereitstellen.
Einrichten
Bevor Sie Funktionen in Ihrem Anmeldedatenanbieter implementieren, führen Sie die Einrichtungsschritte in den folgenden Abschnitten aus.
Abhängigkeiten deklarieren
Deklarieren Sie in der build.gradle
-Datei Ihres Moduls eine Abhängigkeit mit der aktuellen Version der Anmeldedaten-Manager-Bibliothek:
implementation "androidx.credentials:credentials:1.2.0-{latest}"
Dienstelement in Manifestdatei deklarieren
Füge in die Manifestdatei AndroidManifest.xml
deiner App ein <service>
ein.
-Deklaration für eine Dienstklasse, die die
CredentialProviderService
aus der Bibliothek „androidx.credentials“ verwenden,
wie im Beispiel unten gezeigt.
<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>
Die oben genannten Berechtigungs- und Intent-Filter sind entscheidend, damit der Ablauf des Anmeldedaten-Managers wie erwartet funktioniert. Die Berechtigung ist erforderlich, damit nur das Android-System eine Bindung an diesen Dienst vornehmen kann. Der Intent-Filter wird für die Sichtbarkeit dieses Dienstes als Anmeldedatenanbieter, der von Anmeldedaten-Manager
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
Der Dienst unterstützt beispielsweise
herkömmliche Passwörter und Passkeys.
Konstanten, für die
TYPE_PASSWORD_CREDENTIAL
und TYPE_PUBLIC_KEY_CREDENTIAL
:
<?xml version="1.0" encoding="utf-8"?>
<credential-provider xmlns:android="http://schemas.android.com/apk/res/android">
<capabilities>
<capability name="android.credentials.TYPE_PASSWORD_CREDENTIAL" />
<capability name="androidx.credentials.TYPE_PUBLIC_KEY_CREDENTIAL" />
</capabilities>
</credential-provider>
Auf früheren API-Ebenen werden Anmeldedatenanbieter in APIs wie Autofill für Passwörter und andere Daten eingebunden. Diese Anbieter können dieselben internen um die bestehenden Anmeldedatentypen zu speichern und gleichzeitig einschließlich Passkeys.
Zweistufiger Ansatz für die Interaktion mit Dienstleistern
Die Anmeldedatenverwaltung interagiert mit Anmeldedatenanbietern in zwei Phasen:
- Die erste Phase ist die Start-/Abfragephase, in der sich das System an
Dienste und Aufrufe von Anmeldedatenanbietern
onBeginGetCredentialRequest()
,onBeginCreateCredentialRequest()
oderonClearCredentialStateRequest()
-Methoden mitBegin…
-Anfragen. Anbieter müssen diese Anfragen verarbeiten und mitBegin…
-Antworten antworten. Sie werden mit Einträgen gefüllt, die visuelle Optionen darstellen, die auf die Kontoauswahl. Für jeden Eintrag muss einPendingIntent
festgelegt sein. - Sobald der Nutzer einen Eintrag ausgewählt hat, beginnt die Auswahlphase und das mit dem Eintrag verknüpfte
PendingIntent
-Ereignis wird ausgelöst. Daraufhin wird die entsprechende Anbieteraktivität angezeigt. Sobald der Nutzer mit dieser Aktivität fertig ist, muss der Anmeldedatenanbieter die Antwort auf das Ergebnis der Aktivität festlegen, bevor er sie beendet. Diese Antwort wird dann an die Client-App gesendet, die den Anmeldedaten-Manager aufgerufen haben.
Passkey-Erstellung verarbeiten
Abfragen beim Erstellen von Passkeys verarbeiten
Wenn eine Client-App einen Passkey erstellen und bei einem Anmeldedatenanbieter speichern möchte, ruft sie die createCredential
API auf. Um das Problem zu beheben
Ihres Anmeldedatenanbieters an, sodass der Passkey tatsächlich
die im Speicher abgelegt sind, führen Sie die in den folgenden Abschnitten beschriebenen Schritte aus.
- Methode
onBeginCreateCredentialRequest()
in Ihrem Dienst überschreiben verlängert vonCredentialProviderService
. - Verarbeite die
BeginCreateCredentialRequest
, indem du eine entsprechendeBeginCreateCredentialResponse
erstellst und sie über den Callback weitergibst. - Fügen Sie beim Erstellen der
BeginCreateCredentialResponse
die erforderlicheCreateEntries
hinzu. JedeCreateEntry
sollte einem Konto entsprechen, in dem die Anmeldedaten gespeichert werden können. Außerdem mussPendingIntent
zusammen mit anderen erforderlichen Metadaten festgelegt sein.
Das folgende Beispiel zeigt, wie diese Schritte implementiert werden.
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 PendingIntent
-Konstruktion muss Folgendes erfüllen:
- Die entsprechende Aktivität sollte so eingerichtet sein, dass alle erforderlichen biometrischen Aufforderungen, Bestätigungen oder Auswahlen angezeigt werden.
- Alle erforderlichen Daten, die der Anbieter beim Aufrufen der entsprechenden Aktivität benötigt, sollten als Extra für den Intent festgelegt werden, mit dem Ihre
PendingIntent
erstellt wird, z. B. einaccountId
im Erstellungsvorgang. - Dein
PendingIntent
muss mit dem Flag erstellt werdenPendingIntent.FLAG_MUTABLE
, damit das System die letzte zusätzliche Anfrage an den Intent. PendingIntent
darf nicht mit dem FlagPendingIntent.FLAG_ONE_SHOT
erstellt werden, da der Nutzer einen Eintrag auswählen, zurückgehen und ihn noch einmal auswählen kann, was dazu führt, dassPendingIntent
zweimal ausgelöst wird.- Ihre
PendingIntent
muss mit einem eindeutigen Anfragecode erstellt werden, damit jeder Eintrag eine eigene entsprechendePendingIntent
haben kann.
Auswahl von Einträgen für Anfragen zum Erstellen von Passkeys verarbeiten
- Wenn der Nutzer eine zuvor ausgefüllte
CreateEntry
auswählt, wird das Feld entsprechendePendingIntent
aufgerufen und der zugehörige AnbieterActivity
wird erstellt. - Nachdem die
onCreate
-Methode Ihrer Aktivität aufgerufen wurde, greifen Sie auf den zugehörigen Intent zu und übergeben Sie ihn an diePendingIntentHander
-Klasse, um dieProviderCreateCredentialRequest
zu erhalten. - Extrahieren Sie
requestJson
,callingAppInfo
undclientDataHash
aus dem - Extrahieren Sie die lokale
accountId
aus dem Intent-Extra. Dies ist eine beispielhafte App-spezifische Implementierung und ist nicht erforderlich. Diese Konto-ID kann verwendet werden um diese Anmeldedaten für diese bestimmte Konto-ID zu speichern. - Prüfen Sie die
requestJson
. Im folgenden Beispiel werden lokale Datenklassen wiePublicKeyCredentialCreationOptions
verwendet, um die Eingabe-JSON-Datei in eine strukturierte Klasse gemäß der WebAuthn-Spezifikation umzuwandeln. Als Anmeldedatenanbieter können Sie dies durch Ihren eigenen Parser ersetzen. - Überprüfen Sie den asset-link für die anrufende App, falls der Anruf von einer native Android-App.
- Eine Authentifizierungsaufforderung einblenden Im folgenden Beispiel wird die Biometric API von Android verwendet.
- Wenn die Authentifizierung erfolgreich ist, generieren Sie
credentialId
und einen Schlüsselpaar. - Speichern Sie den privaten Schlüssel in Ihrer lokalen Datenbank unter
callingAppInfo.packageName
. - Erstellen Sie eine Web Authentication API-JSON-Antwort, die
besteht aus dem öffentlichen Schlüssel und dem
credentialId
. Im Beispiel unten werden lokale Dienstprogrammklassen wieAuthenticatorAttestationResponse
undFidoPublicKeyCredential
verwendet, die beim Erstellen einer JSON-Datei auf der Grundlage der oben genannten Spezifikation helfen. Als Anmeldedatenanbieter können Sie diese Klassen durch eigene Builder ersetzen. - Erstellen Sie eine
CreatePublicKeyCredentialResponse
mit dem oben generierten JSON. - Legen Sie
CreatePublicKeyCredentialResponse
überPendingIntentHander.setCreateCredentialResponse()
als Extra für eineIntent
fest und legen Sie diese Absicht als Ergebnis der Aktivität fest. - Schließen Sie die Aktivität ab.
Das folgende Codebeispiel veranschaulicht diese Schritte. Dieser Code muss in
Ihre Activity-Klasse, sobald onCreate()
aufgerufen wurde.
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)}"
}
Abfragen für Anfragen zum Erstellen von Passwörtern verarbeiten
So beantwortest du Anfragen zum Erstellen von Passwörtern:
- Informationen zur
processCreateCredentialRequest()
-Methode, die in den Im vorherigen Abschnitt ein weiteres Case in das Schalterblock einfügen Passwortanforderungen. - Fügen Sie beim Erstellen der
BeginCreateCredentialResponse
die erforderlichenCreateEntries
hinzu. - Jede
CreateEntry
sollte einem Konto entsprechen, in dem die Anmeldedaten gespeichert werden können. und es muss einPendingIntent
zusammen mit anderen Metadaten festgelegt sein.
Das folgende Beispiel veranschaulicht die Implementierung dieser Schritte:
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)
}
Eintragsauswahl für Anfragen zur Passworterstellung verarbeiten
Wenn der Nutzer ein ausgefülltes CreateEntry
-Element auswählt, wird das entsprechende
PendingIntent
führt die zugehörige Aktivität aus und ruft sie auf. Öffnen Sie die
übergebenen Intent in onCreate
übergeben und an den
PendingIntentHander
, um die Methode ProviderCreateCredentialRequest
abzurufen.
Das folgende Beispiel zeigt, wie dieser Prozess implementiert wird. Dieser Code muss in der onCreate()
-Methode Ihrer Aktivität verarbeitet werden.
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()
Nutzeranmeldung verwalten
Die Nutzeranmeldung wird so verarbeitet:
- Wenn eine Client-App versucht, einen Nutzer anzumelden, wird eine
GetCredentialRequest
-Instanz vorbereitet. - Das Android-Framework leitet diese Anfrage an alle anwendbaren Anmeldedatenanbieter weiter, indem es eine Bindung an diese Dienste herstellt.
- Der Anbieterdienst erhält dann eine
BeginGetCredentialRequest
mit einer Liste vonBeginGetCredentialOption
, die jeweils Parameter enthalten, mit denen übereinstimmende Anmeldedaten abgerufen werden können.
Um diese Anfrage in Ihrem Anmeldedatenanbieter-Dienst zu verarbeiten, führen Sie die folgenden Schritten:
Überschreiben Sie die Methode
onBeginGetCredentialRequest()
, um die Anfrage zu verarbeiten. Wenn deine Anmeldedaten gesperrt sind, kannst du sofort eineAuthenticationAction
in der Antwort festlegen und den Rückruf 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, die das Entsperren der Anmeldedaten vor der Rückgabe von
credentialEntries
erfordern, müssen einen ausstehenden Intent einrichten, der den Nutzer zum Entsperren 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
CredentialEntries
so ein, dass sie in der Auswahl angezeigt werden. Für Passkeys können Sie Folgendes festlegen:credentialId
als Bonus für den Intent, um zu erfahren, welche Anmeldedaten er verwendet. wird zugeordnet, 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 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) }
Fragen Sie Anmeldedaten aus Ihrer Datenbank ab, erstellen Sie Passkey- und Passworteinträge für dargestellt wird.
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) ) }
Nachdem du die Anmeldedaten abgefragt und eingefügt hast, musst du die Auswahlphase für die vom Nutzer ausgewählten Anmeldedaten verarbeiten, unabhängig davon, ob es sich um einen Passkey oder ein Passwort handelt.
Nutzerauswahl für Passkeys verarbeiten
- Rufe in der
onCreate
-Methode der entsprechenden Activity-Klasse den verknüpfter Intent und übergeben SiePendingIntentHandler.retrieveProviderGetCredentialRequest()
- Extrahieren Sie die
GetPublicKeyCredentialOption
aus der oben abgerufenen Anfrage. Extrahieren Sie anschließendrequestJson
undclientDataHash
von dieser Option aus. - Extrahieren Sie
credentialId
aus dem zusätzlichen Intent, der vom Anmeldedatenanbieter beim Einrichten der entsprechendenPendingIntent
. - Extrahieren Sie den Passkey mithilfe der oben genannten Anfrageparameter aus Ihrer lokalen Datenbank.
Mit extrahierten Metadaten und dem Nutzer bestätigen, dass der Passkey gültig ist Überprüfung.
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 )
Um den Nutzer zu bestätigen, können Sie eine biometrische Aufforderung oder eine andere Bestätigungsmethode anzeigen. Im folgenden Code-Snippet wird die Android Biometric API verwendet.
Wenn die Authentifizierung erfolgreich war, erstellen Sie eine JSON-Antwort gemäß der W3-Spezifikation für Web Authentication Assertion. Im folgenden Code-Snippet werden Hilfsdatenklassen wie
AuthenticatorAssertionResponse
verwendet, um strukturierte Parameter aufzunehmen und in das erforderliche JSON-Format umzuwandeln. Die Antwort enthält eine digitale Signatur des privater Schlüssel von WebAuthn-Anmeldedaten. Der Server der vertrauenden Partei kann diese Signatur überprüfen, um einen Nutzer vor der Anmeldung zu authentifizieren.Erstelle mithilfe der oben generierten JSON-Datei ein
PublicKeyCredential
und setze es auf ein endgültigesGetCredentialResponse
. Diese endgültige Antwort festlegen auf das Ergebnis dieser Aktivität.
Das folgende Beispiel zeigt, wie diese Schritte implementiert werden können:
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)
Nutzerauswahl für die Passwortauthentifizierung
- Greifen Sie in der entsprechenden Aktivität auf den Intent zu, der an
onCreate
übergeben wurde. und extrahieren Sie dieProviderGetCredentialRequest
mitPendingIntentHandler
Verwenden Sie
GetPasswordOption
in der Anfrage zum Abrufen des Passworts Anmeldedaten für den eingehenden Paketnamen.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 } }
Nachdem Sie die Antwort abgerufen haben, legen Sie sie für die ausgewählten Anmeldedaten 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 Authentifizierungsaktionseintrags verarbeiten
Wie bereits erwähnt, kann ein Anmeldedatenanbieter eine AuthenticationAction
festlegen, wenn die Anmeldedaten gesperrt sind. Wenn der Nutzer diese Option auswählt
ist die Aktivität, die der Intent-Aktion im
PendingIntent
wird aufgerufen. Anmeldedatenanbieter können dann eine biometrische
oder einen ähnlichen Mechanismus zum Entsperren der Anmeldedaten. Bei Erfolg
muss der Anmeldedatenanbieter eine BeginGetCredentialResponse
erstellen, ähnlich
wie oben beschrieben wird, da die Anmeldedaten jetzt
entriegelt. Diese Antwort muss dann über die
Methode PendingIntentHandler.setBeginGetCredentialResponse()
vorher
Der vorbereitete Intent wird als Ergebnis festgelegt und die Aktivität ist abgeschlossen.
Anmeldedatenanfragen löschen
Eine Client-App kann anfordern, dass jeder für die Auswahl von Anmeldedaten beibehaltene Status erforderlich ist
z. B. dass sich der Anmeldedatenanbieter an die zuvor ausgewählten
Anmeldedaten und geben diese
nur beim nächsten Mal wieder. Eine Client-App ruft diese API auf und
erwartet, dass die fixierte Auswahl gelöscht wird. Der Anmeldeinformationsanbieterdienst kann diese Anfrage bearbeiten, indem er die Methode onClearCredentialStateRequest()
überschreibt:
override fun onClearCredentialStateRequest(
request: android.service.credentials.ClearCredentialStateRequest,
cancellationSignal: CancellationSignal,
callback: OutcomeReceiver<Void?, ClearCredentialException>,
) {
// Delete any maintained state as appropriate.
}
Möglichkeit zum Verknüpfen mit der Seite „Einstellungen“ des Anbieters hinzufügen
Damit Ihre Nutzer die Einstellungen Ihres Anbieters im Bereich Passwörter,
Passkeys und Autofill-Bildschirm angezeigt wird, sollten die Apps des Anmeldedatenanbieters die
credential-provider
settingsActivity
-Manifestattribut in
res/xml/provider.xml
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 den Namen eines Anbieters klickt. Legen Sie für den Wert dieses Attributs die
Name der Aktivität, 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>
Einstellungs-Intents
Einstellungen öffnen: Das android.settings.CREDENTIAL_PROVIDER
.
öffnet sich ein Einstellungsbildschirm,
auf dem die Nutzenden ihre bevorzugten
zusätzliche Anbieter von Anmeldedaten.
Bevorzugter Anmeldedatendienst: der
ACTION_REQUEST_SET_AUTOFILL_SERVICE
-Intent leitet den Nutzer an den
Auswahlbildschirm für den bevorzugten Anbieter. Der auf diesem Bildschirm ausgewählte Anbieter
werden die bevorzugten Anmeldedaten
und Autofill-Anbieter.
Zulassungsliste mit privilegierten Apps abrufen
Berechtigte Apps wie Webbrowser führen im Namen anderer vertrauenswürdiger Seiten Aufrufe des Anmeldedaten-Managers aus, indem sie den Parameter origin
in den Methoden GetCredentialRequest()
und CreatePublicKeyCredentialRequest()
des Anmeldedaten-Managers festlegen. Um diese Anträge zu bearbeiten,
Der Anmeldedatenanbieter ruft die origin
mithilfe des getOrigin()
ab.
der API erstellen.
Um die origin
abzurufen, muss die Anmeldedatenanbieter-App eine Liste der privilegierten und vertrauenswürdigen Anrufer an die androidx.credentials.provider.CallingAppInfo's getOrigin()
API übergeben. Diese Zulassungsliste
muss ein gültiges JSON-Objekt sein. origin
wird zurückgegeben, wenn die packageName
und
Die von signingInfo
abgerufenen Zertifikatfingerabdrücke stimmen mit denen einer App überein
finden Sie in der privilegedAllowlist
, die an die getOrigin()
API übergeben wurde. Nachdem der Wert origin
abgerufen wurde, sollte die Anbieter-App dies als privilegierten Aufruf betrachten und origin
in den Clientdaten in AuthenticatorResponse
festlegen, anstatt origin
anhand der Signatur der anrufenden App zu berechnen.
Wenn du ein origin
abrufst, verwende das clientDataHash
, das direkt zur Verfügung gestellt wird
in CreatePublicKeyCredentialRequest()
oder
GetPublicKeyCredentialOption()
anstelle von Assembler und Hashing
clientDataJSON
. Legen Sie in der Attestierungs- und Bestätigungsantwort einen Platzhalterwert für clientDataJSON
fest, um Probleme beim JSON-Parsen zu vermeiden.
Der Google Passwortmanager verwendet eine frei verfügbare Zulassungsliste für
getOrigin()
-Aufrufe Als Anmeldedatenanbieter können Sie diese Liste verwenden oder eine eigene im von der API beschriebenen JSON-Format angeben. Es liegt an der
Anbieter auswählen, welche Liste verwendet werden soll. Privilegierten Zugriff über Drittanbieter erhalten
Anmeldedatenanbieter 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 > Mein Anbieter > Aktivieren oder deaktivieren.
fun createSettingsPendingIntent(): PendingIntent