Credential Manager bezieht sich auf eine Reihe von APIs, die in Android 14 eingeführt wurden und mehrere Anmeldemethoden wie Nutzername und Passwort, Passkeys und Verbundanmeldelösungen (z. B. „Über Google anmelden“) unterstützen. 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.
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 neuesten Version der Credential Manager-Bibliothek:
implementation "androidx.credentials:credentials:1.2.0-{latest}"
Dienstelement in der Manifestdatei deklarieren
Fügen Sie in die Manifestdatei AndroidManifest.xml
Ihrer App 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="<any drawable icon>"
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-Ablauf 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 für den Credential Manager erkannt wird.
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, indem Sie Konstanten verwenden, 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 als TYPE_PASSWORD_CREDENTIAL
und TYPE_PUBLIC_KEY_CREDENTIAL
definiert:
<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>
Bei früheren API-Levels werden Anmeldedatenanbieter in APIs wie „Autofill für Passwörter und andere Daten“ eingebunden. Diese Anbieter können dieselbe interne Infrastruktur zum Speichern der vorhandenen Anmeldedatentypen verwenden und sie erweitern, um andere, einschließlich Passkeys, zu unterstützen.
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 an Anmeldedienstanbieter gebunden wird und die Methoden
onBeginGetCredentialRequest()
,onBeginCreateCredentialRequest()
oderonClearCredentialStateRequest()
mitBegin…
-Anfragen aufgerufen werden. Anbieter müssen diese Anfragen verarbeiten und mitBegin…
-Antworten antworten, die mit Einträgen gefüllt sind, die visuelle Optionen darstellen, die in der Kontoauswahl angezeigt werden sollen. Jeder Eintrag muss einPendingIntent
-Element haben. - Sobald der Nutzer einen Eintrag auswählt, beginnt die Auswahlphase und das mit dem Eintrag verknüpfte
PendingIntent
wird ausgelöst, wodurch die entsprechende Anbieteraktivität aufgerufen wird. Wenn 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 Client-App gesendet, die Credential Manager aufgerufen hat.
Passkey-Erstellung verarbeiten
Anfragen zum Erstellen von Passkeys verarbeiten
Wenn eine Client-App 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 vonCredentialProviderService
abgeleitet ist. - Verarbeite das
BeginCreateCredentialRequest
, indem du ein entsprechendesBeginCreateCredentialResponse
erstellst und es über den Callback übergibst. - Fügen Sie beim Erstellen von
BeginCreateCredentialResponse
die erforderlichenCreateEntries
hinzu. JedesCreateEntry
sollte einem Konto entsprechen, in dem die Anmeldedaten gespeichert werden können, und muss zusammen mit anderen erforderlichen Metadaten einPendingIntent
haben.
Das folgende Beispiel veranschaulicht, 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
)
)
}
Ihre PendingIntent
-Konstruktion sollte die folgenden Anforderungen 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 benötigt, wenn die entsprechende Aktivität aufgerufen wird, sollten als Extra für den Intent festgelegt werden, der zum Erstellen Ihres
PendingIntent
verwendet wird, z. B. einaccountId
im Erstellungsprozess. - Ihr
PendingIntent
muss mit dem FlagPendingIntent.FLAG_MUTABLE
erstellt werden, damit das System die endgültige Anfrage an das Intent-Extra anhängen kann. - Ihr
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. In diesem Fall wird dasPendingIntent
zweimal ausgelöst. - Ihr
PendingIntent
muss mit einem eindeutigen Anfragecode erstellt werden, damit jeder Eintrag einen eigenen entsprechendenPendingIntent
haben kann.
Eintragsauswahl für Anfragen zum Erstellen von Passkeys verarbeiten
- Wenn der Nutzer ein zuvor ausgefülltes
CreateEntry
auswählt, wird das entsprechendePendingIntent
aufgerufen und der zugehörige AnbieterActivity
erstellt. - Nachdem die Methode
onCreate
Ihrer Aktivität aufgerufen wurde, greifen Sie auf den zugehörigen Intent zu und übergeben Sie ihn an die KlassePendingIntentHander
, um dieProviderCreateCredentialRequest
abzurufen. - Extrahieren Sie
requestJson
,callingAppInfo
undclientDataHash
aus der Anfrage. - Extrahieren Sie die lokale
accountId
aus dem Intent-Extra. Dies ist eine Implementierung, die für die Beispiel-App spezifisch ist und nicht erforderlich ist. Mit dieser Konto-ID können Sie die Anmeldedaten für dieses Konto speichern. - Validieren Sie
requestJson
. Im folgenden Beispiel werden lokale Datenklassen wiePublicKeyCredentialCreationOptions
verwendet, um die eingegebene JSON-Datei 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 Anruf-App, wenn der Anruf von einer nativen Android-App stammt.
- Eine Authentifizierungsaufforderung anzeigen Im folgenden Beispiel wird die Android-Biometric API verwendet.
- Wenn die Authentifizierung erfolgreich ist, generieren Sie ein
credentialId
und ein Schlüsselpaar. - Speichern Sie den privaten Schlüssel in Ihrer lokalen Datenbank gegen
callingAppInfo.packageName
. - Erstellen Sie eine JSON-Antwort der Web Authentication API, die aus dem öffentlichen Schlüssel und dem
credentialId
besteht. Im Beispiel unten werden lokale Hilfsklassen wieAuthenticatorAttestationResponse
undFidoPublicKeyCredential
verwendet, um ein JSON-Objekt basierend auf der oben genannten Spezifikation zu erstellen.Als Anmeldedatenanbieter können Sie diese Klassen durch Ihre eigenen Builder ersetzen. - Erstellen Sie eine
CreatePublicKeyCredentialResponse
mit dem oben generierten JSON-Code. - Legen Sie
CreatePublicKeyCredentialResponse
als Extra für einenIntent
überPendingIntentHander.setCreateCredentialResponse()
fest und legen Sie diesen Intent als Ergebnis der Aktivität fest. - Schließe die Aktivität ab.
Das folgende Codebeispiel veranschaulicht diese Schritte. Dieser Code muss in Ihrer Activity-Klasse 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)}"
}
Anfragen zur Erstellung von Passwörtern bearbeiten
So gehen Sie bei Anfragen zum Erstellen von Passwörtern vor:
- Fügen Sie in der im vorherigen Abschnitt erwähnten Methode
processCreateCredentialRequest()
einen weiteren Fall im Switch-Block hinzu, um Passwortanfragen zu verarbeiten. - Fügen Sie beim Erstellen von
BeginCreateCredentialResponse
die erforderlichenCreateEntries
hinzu. - Jedes
CreateEntry
sollte einem Konto entsprechen, in dem die Anmeldedaten gespeichert werden können, und muss zusammen mit anderen Metadaten einPendingIntent
haben.
Das folgende Beispiel veranschaulicht, wie diese Schritte implementiert werden:
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)
}
Eintragsauswahl für Anfragen zur Passwortgenerierung verarbeiten
Wenn der Nutzer ein ausgefülltes CreateEntry
auswählt, wird das entsprechende PendingIntent
ausgeführt und die zugehörige Aktivität wird aufgerufen. Greifen Sie auf die zugehörige Absicht zu, die in onCreate
übergeben wurde, und übergeben Sie sie an die Klasse PendingIntentHander
, um die Methode ProviderCreateCredentialRequest
abzurufen.
Das folgende Beispiel veranschaulicht, 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 erfolgt in den folgenden Schritten:
- 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 Verbindung zu diesen Diensten herstellt.
- Der Anbieterdienst empfängt dann eine
BeginGetCredentialRequest
, die eine Liste vonBeginGetCredentialOption
enthält. JedeBeginGetCredentialOption
enthält Parameter, mit denen passende Anmeldedaten abgerufen werden können.
Führen Sie die folgenden Schritte aus, um diese Anfrage in Ihrem Anmeldedienstanbieter zu verarbeiten:
Überschreiben Sie die Methode
onBeginGetCredentialRequest()
, um die Anfrage zu verarbeiten. Wenn Ihre Anmeldedaten gesperrt sind, können Sie sofort einenAuthenticationAction
fü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, die die Anmeldedaten entsperren müssen, bevor sie
credentialEntries
zurückgeben, müssen einen PendingIntent einrichten, der den Nutzer zum Entsperrvorgang 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 die Anmeldedaten aus Ihrer lokalen Datenbank ab und richten Sie sie mit
CredentialEntries
ein, damit sie in der Auswahl angezeigt werden. Für Passkeys können SiecredentialId
als Extra für den Intent festlegen, um zu wissen, welchen Anmeldedaten er 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) }
Anmeldedaten aus Ihrer Datenbank abfragen, Passkey- und Passworteinträge zum Ausfüllen erstellen
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 eingefügt haben, müssen Sie nun 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
- Rufen Sie in der Methode
onCreate
der entsprechenden Aktivität den zugehörigen Intent ab und übergeben Sie ihn anPendingIntentHandler.retrieveProviderGetCredentialRequest()
. - Extrahieren Sie die
GetPublicKeyCredentialOption
aus der oben abgerufenen Anfrage. Extrahieren Sie anschließend dierequestJson
undclientDataHash
aus dieser Option. - Extrahieren Sie die
credentialId
aus dem Intent-Extra, das vom Anmeldedatenanbieter bei der Einrichtung des entsprechendenPendingIntent
ausgefüllt wurde. - Extrahieren Sie den Passkey aus Ihrer lokalen Datenbank mithilfe der oben aufgerufenen Anfrageparameter.
Prüfen Sie, ob 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 bestätigen, wird eine biometrische Aufforderung (oder eine andere Bestätigungsmethode) angezeigt. Im folgenden Code-Snippet wird die Android Biometric API verwendet.
Sobald die Authentifizierung erfolgreich ist, erstellen Sie eine JSON-Antwort basierend auf der W3C Web Authentication Assertion-Spezifikation. Im folgenden Code-Snippet werden Hilfsdatenklassen wie
AuthenticatorAssertionResponse
verwendet, 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 vertrauenden Partei kann diese Signatur überprüfen, um einen Nutzer vor der Anmeldung zu authentifizieren.Erstellen Sie mit dem oben generierten JSON-Code ein
PublicKeyCredential
und legen Sie es für ein endgültigesGetCredentialResponse
fest. Formuliere diese endgültige Antwort auf Grundlage des Ergebnisses dieser Aktivität.
Das folgende Beispiel veranschaulicht, 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)
Umgang mit der Nutzerauswahl für die Passwortauthentifizierung
- Greifen Sie in der entsprechenden Aktivität auf den Intent zu, der an
onCreate
übergeben wurde, und extrahieren SieProviderGetCredentialRequest
mitPendingIntentHandler
. Verwenden Sie
GetPasswordOption
in der Anfrage, um die Passwortanmeldedaten 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 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 ein AuthenticationAction
festlegen, wenn die Anmeldedaten gesperrt sind. Wenn der Nutzer diesen Eintrag auswählt, wird die Aktivität aufgerufen, die der im PendingIntent
festgelegten Intent-Aktion entspricht. Anmeldedatenanbieter können dann einen biometrischen Authentifizierungsvorgang oder einen ähnlichen Mechanismus zum Entsperren der Anmeldedaten anzeigen. Bei Erfolg muss der Anmeldedatenanbieter ein BeginGetCredentialResponse
erstellen, ähnlich wie oben für die Nutzeranmeldung beschrieben, da die Anmeldedaten jetzt entsperrt sind. Diese Antwort muss dann über die Methode PendingIntentHandler.setBeginGetCredentialResponse()
festgelegt werden, bevor die vorbereitete Intention als Ergebnis festgelegt und die Aktivität beendet wird.
Anmeldedatenanfragen löschen
Eine Client-App kann anfordern, dass der für die Auswahl von Anmeldedaten verwaltete Status gelöscht wird. 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 klebrige Auswahl gelöscht wird. Ihr Anmeldeinformationsanbieterdienst kann diese Anfrage verarbeiten, indem er die Methode onClearCredentialStateRequest()
überschreibt:
override fun onClearCredentialStateRequest(
request: ProviderClearCredentialStateRequest,
cancellationSignal: CancellationSignal,
callback: OutcomeReceiver<Void?, ClearCredentialException>
) {
// Delete any maintained state as appropriate.
}
Möglichkeit, zur Einstellungsseite des Anbieters zu verlinken
Damit Nutzer die Einstellungen Ihres Anbieters über den Bildschirm Passwörter, Passkeys und Autofill öffnen können, sollten Anmeldedienstanbieter-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 unter 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-Intents
Einstellungen öffnen: Mit dem Intent android.settings.CREDENTIAL_PROVIDER
wird ein Einstellungsbildschirm aufgerufen, auf dem der Nutzer seine bevorzugten und zusätzlichen Anmeldedatenanbieter auswählen kann.

Bevorzugter Anmeldedienst: Der Intent ACTION_REQUEST_SET_AUTOFILL_SERVICE
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 vertrauender Parteien auf, indem sie den Parameter origin
in den Credential Manager-Methoden GetCredentialRequest()
und CreatePublicKeyCredentialRequest()
festlegen. Zur Verarbeitung dieser Anfragen ruft der Anmeldedatenanbieter das origin
über die getOrigin()
API ab.
Um die 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. Die origin
wird zurückgegeben, wenn die packageName
und die von signingInfo
abgerufenen Zertifikatsfingerabdrücke mit denen einer App übereinstimmen, die in der privilegedAllowlist
gefunden wurde, die an die getOrigin()
API übergeben wurde. Nachdem der Wert origin
abgerufen wurde, sollte die Anbieter-App dies als privilegierten Aufruf betrachten und diesen origin
für die 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()
bereitgestellt wird, anstatt clientDataJSON
während der Signaturanfrage zusammenzustellen und zu hashen. Um Probleme beim Parsen von JSON zu vermeiden, legen Sie in der Attestierungs- und Zusicherungsantwort 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 eine eigene im von der API beschriebenen JSON-Format bereitstellen. Es liegt im Ermessen des Anbieters, welche Liste verwendet wird. Informationen zum Erhalten von privilegiertem Zugriff mit Anmeldedaten 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 & Konten > Ihr Anbieter > Aktivieren oder Deaktivieren aktivieren.
auf, um bei Aufruf einen PendingIntent zurückzugeben. Dadurch wird ein Bildschirm angezeigt, auf dem ein Nutzer Ihren Credential Manager-Anbieter aktivieren kann.fun createSettingsPendingIntent(): PendingIntent