1. Hinweis
Herkömmliche Authentifizierungslösungen bergen eine Reihe von Herausforderungen in puncto Sicherheit und Nutzerfreundlichkeit.
Passwörter sind weit verbreitet, aber...
- Einfach vergessen
- Nutzer benötigen Kenntnisse, um starke Passwörter zu erstellen.
- Leichtes Phishing, Ernten und erneutes Abspielen durch Angreifer.
Android hat an der Entwicklung der Credential Manager API gearbeitet, um die Anmeldung zu vereinfachen und Sicherheitsrisiken zu minimieren. Dazu werden Passkeys unterstützt, der Branchenstandard der nächsten Generation für die Authentifizierung ohne Passwort.
Im Anmeldedaten-Manager werden Passkeys unterstützt und mit herkömmlichen Authentifizierungsmethoden wie Passwörtern, der Funktion „Über Google anmelden“ usw. kombiniert.
Nutzer können Passkeys erstellen und im Google Passwortmanager speichern. Dadurch werden sie zwischen den Android-Geräten synchronisiert, auf denen der Nutzer angemeldet ist. Ein Passkey muss erstellt und mit einem Nutzerkonto verknüpft sein. Außerdem muss sein öffentlicher Schlüssel auf einem Server gespeichert sein, damit sich ein Nutzer damit anmelden kann.
In diesem Codelab erfahren Sie, wie Sie sich über die Credential Manager API mit Passkeys und Passwörtern registrieren und für zukünftige Authentifizierungszwecke verwenden. Es gibt zwei Abläufe:
- Anmelden : mit Passkeys und Passwort
- Anmelden : mit Passkeys und gespeichertes Passwort.
Vorbereitung
- Grundlegendes Verständnis zum Ausführen von Apps in Android Studio
- Grundlegendes Verständnis des Authentifizierungsvorgangs in Android-Apps
- Grundlegendes Verständnis von Passkeys
Lerninhalte
- So erstellen Sie einen Passkey.
- Passwort im Passwortmanager speichern
- Nutzer mit einem Passkey oder einem gespeicherten Passwort authentifizieren
Voraussetzungen
Eine der folgenden Gerätekombinationen:
- Ein Android-Gerät mit Android 9 oder höher (für Passkeys) und Android 4.4 oder höher(für die Passwortauthentifizierung über die Credential Manager API).
- Gerät mit biometrischem Sensor.
- Achte darauf, eine biometrische Anmeldung oder eine Displaysperre einzurichten.
- Kotlin-Plug-in-Version : 1.8.10
2. Einrichten
- Klonen Sie dieses Repository aus dem Branch credman_codelab auf Ihren Laptop : https://github.com/android/identity-samples/tree/credman_codelab
- Rufe das Modul CredentialManager auf und öffne das Projekt in Android Studio.
Anfangszustand der App prüfen
Gehen Sie wie folgt vor, um zu sehen, wie der Anfangszustand der App funktioniert:
- Starten Sie die App.
- Es wird ein Hauptbildschirm mit der Schaltfläche „Registrieren und Anmelden“ angezeigt.
- Sie können auf „Registrieren“ klicken, um sich mit einem Passkey oder einem Passwort zu registrieren.
- Sie können auf „Anmelden“ klicken, um sich mit Passkey und gespeichertes Passwort.
Informationen dazu, was Passkeys sind und wie sie funktionieren, findest du unter Wie funktionieren Passkeys? .
3. Möglichkeit zur Registrierung mit Passkeys hinzufügen
Wenn sich Nutzer in einer Android-App, die die Credential Manager API verwendet, für ein neues Konto registrieren, können sie einen Passkey für ihr Konto erstellen. Dieser Passkey wird sicher beim Anmeldedatenanbieter gespeichert und für zukünftige Anmeldungen verwendet, ohne dass der Nutzer jedes Mal sein Passwort eingeben muss.
Jetzt erstellen Sie einen Passkey und registrieren die Anmeldedaten der Nutzer mithilfe des biometrischen Verfahrens oder der Displaysperre.
Mit Passkey registrieren
Alles über den Anmeldedaten-Manager -> App -> Haupt -> java -> SignUpFragment.kt sehen Sie das Textfeld „username“, und einer Schaltfläche zur Anmeldung mit einem Passkey.
Übergeben Sie die Abfrage und andere JSON-Antworten an den createPasskey()-Aufruf
Bevor ein Passkey erstellt wird, müssen Sie den Server bitten, die erforderlichen Informationen abzurufen, die während des Aufrufs createCredential() an die Credential Manager API übergeben werden.
Zum Glück gibt es in Ihren Assets bereits eine Testantwort(RegFromServer.txt), die solche Parameter in diesem Codelab zurückgibt.
- Gehen Sie in Ihrer App zur Methode SignUpFragment.kt, Find, signUpWithPasskeys, in der Sie die Logik für die Erstellung eines Passkeys und die Einlassung des Nutzers schreiben. Sie finden die Methode in derselben Klasse.
- Überprüfen Sie den else-Block mit einem Kommentar, um createPasskey() aufzurufen, und ersetzen Sie ihn durch folgenden Code :
SignUpFragment.kt
//TODO : Call createPasskey() to signup with passkey
val data = createPasskey()
Diese Methode wird aufgerufen, sobald auf dem Bildschirm ein gültiger Nutzername eingetragen ist.
- Innerhalb der createPasskey()-Methode müssen Sie eine CreatePublicKeyCredentialRequest() erstellen, bei der die erforderlichen Parameter zurückgegeben werden.
SignUpFragment.kt
//TODO create a CreatePublicKeyCredentialRequest() with necessary registration json from server
val request = CreatePublicKeyCredentialRequest(fetchRegistrationJsonFromServer())
Mit dieser Methode FetchRegistrationJsonFromServer() wird die JSON-Registrierungsantwort aus Assets gelesen und die Registrierungs-JSON-Datei zurückgegeben, die beim Erstellen des Passkeys übergeben wird.
- Suchen Sie die Methode "fetchRegistrationJsonFromServer()" und ersetzen Sie die TODO-Datei durch den folgenden Code, um die JSON-Datei zurückzugeben, und entfernen Sie außerdem die leere "return -Anweisung" des Strings :
SignUpFragment.kt
//TODO fetch registration mock response
val response = requireContext().readFromAsset("RegFromServer")
//Update userId,challenge, name and Display name in the mock
return response.replace("<userId>", getEncodedUserId())
.replace("<userName>", binding.username.text.toString())
.replace("<userDisplayName>", binding.username.text.toString())
.replace("<challenge>", getEncodedChallenge())
- Hier lesen Sie die Registrierungs-JSON-Datei aus den Assets.
- Diese JSON-Datei enthält vier Felder, die ersetzt werden müssen.
- Die UserId muss eindeutig sein, damit ein Nutzer bei Bedarf mehrere Passkeys erstellen kann. Sie ersetzen <userId>. durch die generierte Nutzer-ID.
- <challenge> muss außerdem eindeutig sein, damit Sie eine zufällige einmalige Herausforderung generieren. Die Methode ist bereits in Ihrem Code enthalten.
Das folgende Code-Snippet enthält Beispieloptionen, die Sie vom Server erhalten:
{
"challenge": String,
"rp": {
"name": String,
"id": String
},
"user": {
"id": String,
"name": String,
"displayName": String
},
"pubKeyCredParams": [
{
"type": "public-key",
"alg": -7
},
{
"type": "public-key",
"alg": -257
}
],
"timeout": 1800000,
"attestation": "none",
"excludeCredentials": [],
"authenticatorSelection": {
"authenticatorAttachment": "platform",
"requireResidentKey": true,
"residentKey": "required",
"userVerification": "required"
}
}
Die folgende Tabelle ist nicht vollständig, enthält aber die wichtigen Parameter im Wörterbuch PublicKeyCredentialCreationOptions
:
Parameter | Textzeilen |
Ein vom Server generierter zufälliger String, der so viel Entropie enthält, dass ein Erraten nicht möglich ist. Sie sollte mindestens 16 Byte lang sein. Dies ist erforderlich, wird aber während der Registrierung nicht verwendet, es sei denn, du verwendest Attestierung. | |
Die eindeutige ID eines Nutzers. Dieser Wert darf keine personenidentifizierbaren Informationen wie E-Mail-Adressen oder Nutzernamen enthalten. Ein 16-Byte-Zufallswert, der pro Konto generiert wird, ist gut geeignet. | |
Dieses Feld sollte eine eindeutige Kennung für das Konto enthalten, das der Nutzer erkennt, z. B. seine E-Mail-Adresse oder seinen Nutzernamen. Dieser wird in der Kontoauswahl angezeigt. Bei Verwendung eines Nutzernamens muss derselbe Wert wie bei der Passwortauthentifizierung verwendet werden. | |
Dieses Feld ist ein optionaler, benutzerfreundlicherer Name für das Konto. Das ist ein verständlicher Name für das Nutzerkonto, der nur für die Anzeige bestimmt ist. | |
Die Entität der vertrauenden Partei entspricht Ihren Antragsdetails.Sie benötigt Folgendes :
| |
Die Anmeldedatenparameter für den öffentlichen Schlüssel sind eine Liste zulässiger Algorithmen und Schlüsseltypen. Diese Liste muss mindestens ein Element enthalten. | |
Der Nutzer, der ein Gerät registrieren möchte, hat möglicherweise andere Geräte registriert. Wenn Sie die Erstellung mehrerer Anmeldedaten für dasselbe Konto auf einem einzelnen Authenticator einschränken möchten, können Sie diese Geräte ignorieren. Das transports-Mitglied, falls angegeben, sollte das Ergebnis des Aufrufs von getTransports() während der Registrierung der einzelnen Anmeldedaten enthalten. | |
gibt an, ob das Gerät am Bahnsteig befestigt werden soll oder nicht und ob es keine Anforderung gibt. Legen Sie „platform“ fest. Dies bedeutet, dass wir einen Authenticator wünschen, der in das Plattformgerät eingebettet ist, und der Nutzer wird nicht aufgefordert, eine Beispiel-ID einzufügen. einen USB-Sicherheitsschlüssel. | |
| einen Wert als „erforderlich“ um einen Passkey zu erstellen. |
Anmeldedaten erstellen
- Nachdem Sie eine CreatePublicKeyCredentialRequest() erstellt haben, müssen Sie den createCredential()-Aufruf mit der erstellten Anfrage aufrufen.
SignUpFragment.kt
//TODO call createCredential() with createPublicKeyCredentialRequest
try {
response = credentialManager.createCredential(
requireActivity(),
request
) as CreatePublicKeyCredentialResponse
} catch (e: CreateCredentialException) {
configureProgress(View.INVISIBLE)
handlePasskeyFailure(e)
}
- Sie übergeben die erforderlichen Informationen an createCredential().
- Sobald die Anfrage erfolgreich war, wird am unteren Rand des Bildschirms eine Aufforderung angezeigt, einen Passkey zu erstellen.
- Nutzer können jetzt ihre Identität über biometrische Verfahren oder eine Displaysperre bestätigen.
- Sie verarbeiten die Sichtbarkeit der gerenderten Ansichten und die Ausnahmen, wenn die Anfrage aus irgendeinem Grund fehlschlägt oder nicht erfolgreich ist. Hier werden die Fehlermeldungen protokolliert und in einem Fehlerdialogfeld in der App angezeigt. Die vollständigen Fehlerprotokolle können über Android Studio oder den Befehl „adb debug“ angezeigt werden.
- Abschließend müssen Sie den Registrierungsprozess abschließen, indem Sie die Anmeldedaten des öffentlichen Schlüssels an den Server senden und dem Nutzer Zugriff gewähren. Die App empfängt ein Anmeldedatenobjekt, das einen öffentlichen Schlüssel enthält, den Sie an den Server senden können, um den Passkey zu registrieren.
Hier haben wir einen Pseudo-Server verwendet. Also geben wir einfach "true" zurück, um anzugeben, dass der Server den registrierten öffentlichen Schlüssel für zukünftige Authentifizierungs- und Validierungszwecke gespeichert hat.
Suchen Sie in der Methode signUpWithPasskeys() nach einem relevanten Kommentar und ersetzen Sie ihn durch folgenden Code:
SignUpFragment.kt
//TODO : complete the registration process after sending public key credential to your server and let the user in
data?.let {
registerResponse()
DataProvider.setSignedInThroughPasskeys(true)
listener.showHome()
}
- registerResponse gibt "true" zurück, das angibt, dass der (Beispiel-)Server den öffentlichen Schlüssel für die zukünftige Verwendung gespeichert hat.
- Sie setzen das Flag „setSignedInThroughPasskeys“ als „true“, um darauf hinzuweisen, dass Sie sich über Passkeys anmelden.
- Nach der Anmeldung leiten Sie den Nutzer zum Startbildschirm weiter.
Das folgende Code-Snippet enthält ein Beispiel für Optionen, die Sie erhalten sollten:
{
"id": String,
"rawId": String,
"type": "public-key",
"response": {
"clientDataJSON": String,
"attestationObject": String,
}
}
Die folgende Tabelle ist nicht vollständig, enthält aber die wichtigen Parameter für PublicKeyCredential
:
Parameter | Textzeilen |
Eine Base64URL-codierte ID des erstellten Passkeys. Anhand dieser ID kann der Browser feststellen, ob sich bei der Authentifizierung ein passender Passkey auf dem Gerät befindet. Dieser Wert muss in der Datenbank im Back-End gespeichert werden. | |
Eine | |
Ein | |
Ein |
Führen Sie die App aus. Sie können dann auf die Schaltfläche „Mit Passkeys registrieren“ klicken und einen Passkey erstellen.
4. Passwort im Anmeldeinformationsanbieter speichern
In dieser App haben Sie auf dem Anmeldebildschirm bereits eine Anmeldung mit Nutzername und Passwort zu Demonstrationszwecken implementiert.
Um die Anmeldedaten des Nutzerpassworts beim Passwortanbieter zu speichern, implementieren Sie eine CreatePasswordRequest, die an createCredential() übergeben wird, um das Passwort zu speichern.
- Suchen Sie die Methode signUpWithPassword() und ersetzen Sie den TODO-Aufruf zum createPassword :
SignUpFragment.kt
//TODO : Save the user credential password with their password provider
createPassword()
- In der Methode createPassword() müssen Sie eine Passwortanfrage wie diese erstellen und den TODO-Code durch folgenden Code ersetzen :
SignUpFragment.kt
//TODO : CreatePasswordRequest with entered username and password
val request = CreatePasswordRequest(
binding.username.text.toString(),
binding.password.text.toString()
)
- Als Nächstes müssen Sie in der Methode createPassword() Anmeldedaten mit der Anforderung zur Passworterstellung erstellen und die Anmeldeinformationen des Benutzerpassworts beim Passwortanbieter speichern. Ersetzen Sie TODO durch folgenden Code :
SignUpFragment.kt
//TODO : Create credential with created password request
try {
credentialManager.createCredential(request, requireActivity()) as CreatePasswordResponse
} catch (e: Exception) {
Log.e("Auth", " Exception Message : " + e.message)
}
- Sie haben die Anmeldedaten nun erfolgreich beim Passwortanbieter des Nutzers gespeichert, um sich mit nur einem Tippen mit einem Passwort zu authentifizieren.
5. Möglichkeit zur Authentifizierung mit einem Passkey oder Passwort hinzufügen
Jetzt können Sie es als Möglichkeit zur sicheren Authentifizierung bei Ihrer App verwenden.
Wettkampf und andere Optionen abrufen, die für den getPasskey()-Aufruf übergeben werden sollen
Bevor Sie den Nutzer zur Authentifizierung auffordern, müssen Sie Parameter anfordern, um die WebAuthn-JSON-Datei vom Server zu übergeben, einschließlich einer Identitätsbestätigung.
Sie haben bereits eine simulierte Antwort in Ihren Assets(AuthFromServer.txt), die solche Parameter in diesem Codelab zurückgibt.
- Rufen Sie in Ihrer App SignInFragment.kt auf und suchen Sie nach der Methode
signInWithSavedCredentials
. Dort schreiben Sie die Logik für die Authentifizierung über einen gespeicherten Passkey oder ein Passwort und lassen den Nutzer Folgendes eingeben : - Überprüfen Sie den else-Block mit einem Kommentar, um createPasskey() aufzurufen, und ersetzen Sie ihn durch folgenden Code :
SignInFragment.kt
//TODO : Call getSavedCredentials() method to signin using passkey/password
val data = getSavedCredentials()
- In der Methode getSavedCredentials() müssen Sie eine
GetPublicKeyCredentialOption
() mit den erforderlichen Parametern erstellen, die zum Abrufen der Anmeldedaten von Ihrem Anmeldedatenanbieter erforderlich sind.
SigninFragment.kt
//TODO create a GetPublicKeyCredentialOption() with necessary registration json from server
val getPublicKeyCredentialOption =
GetPublicKeyCredentialOption(fetchAuthJsonFromServer(), null)
Mit dieser Methode FetchAuthJsonFromServer() liest die Authentifizierungs-JSON-Antwort aus Assets und gibt die JSON-Authentifizierung zurück, um alle mit diesem Nutzerkonto verknüpften Passkeys abzurufen.
Zweiter Parameter : clientDataHash – ein Hash, mit dem die Identität der vertrauenden Seite überprüft wird. Dieser Parameter wird nur festgelegt, wenn Sie GetCredentialRequest.origin festgelegt haben. Für die Beispielanwendung ist dieser Wert null.
Der dritte Parameter ist "true", wenn der Vorgang sofort zurückgegeben werden soll, wenn keine Anmeldedaten verfügbar sind, anstatt auf die Ermittlung von Remote-Anmeldedaten zurückzugreifen. Andernfalls ist der Parameter "false" (Standardeinstellung).
- Suchen Sie die MethodefetchAuthJsonFromServer() und ersetzen Sie den TODO-Code durch den folgenden Code, um die JSON-Datei zurückzugeben, und entfernen Sie außerdem die leere Zeichenfolge (return)-Anweisung :
SignInFragment.kt
//TODO fetch authentication mock json
return requireContext().readFromAsset("AuthFromServer")
Hinweis : Der Server dieses Codelab ist so konzipiert, dass eine JSON-Datei zurückgegeben wird, die dem Wörterbuch PublicKeyCredentialRequestOptions
, das an den getCredential()-Aufruf der API übergeben wird, so ähnlich wie möglich ist. Das folgende Code-Snippet enthält einige Beispieloptionen, die Sie erhalten sollten:
{
"challenge": String,
"rpId": String,
"userVerification": "",
"timeout": 1800000
}
Die folgende Tabelle ist nicht vollständig, enthält aber die wichtigen Parameter im Wörterbuch PublicKeyCredentialRequestOptions
:
Parameter | Textzeilen |
Eine vom Server generierte Aufgabe in einem | |
Eine RP-ID ist eine Domain. Für eine Website kann entweder ihre Domain oder ein registrierfähiges Suffix angegeben werden. Dieser Wert muss mit dem |
- Als Nächstes müssen Sie ein PasswordOption()-Objekt erstellen, um alle bei Ihrem Passwortanbieter gespeicherten Passwörter über die Credential Manager API für dieses Nutzerkonto abzurufen. Suchen Sie in der Methode getSavedCredentials() das TODO und ersetzen Sie es durch Folgendes :
SigninFragment.kt
//TODO create a PasswordOption to retrieve all the associated user's password
val getPasswordOption = GetPasswordOption()
Anmeldedaten abrufen
- Als Nächstes müssen Sie die getCredential()-Anfrage mit allen oben genannten Optionen aufrufen, um die zugehörigen Anmeldedaten abzurufen :
SignInFragment.kt
//TODO call getCredential() with required credential options
val result = try {
credentialManager.getCredential(
requireActivity(),
GetCredentialRequest(
listOf(
getPublicKeyCredentialOption,
getPasswordOption
)
)
)
} catch (e: Exception) {
configureViews(View.INVISIBLE, true)
Log.e("Auth", "getCredential failed with exception: " + e.message.toString())
activity?.showErrorAlert(
"An error occurred while authenticating through saved credentials. Check logs for additional details"
)
return null
}
if (result.credential is PublicKeyCredential) {
val cred = result.credential as PublicKeyCredential
DataProvider.setSignedInThroughPasskeys(true)
return "Passkey: ${cred.authenticationResponseJson}"
}
if (result.credential is PasswordCredential) {
val cred = result.credential as PasswordCredential
DataProvider.setSignedInThroughPasskeys(false)
return "Got Password - User:${cred.id} Password: ${cred.password}"
}
if (result.credential is CustomCredential) {
//If you are also using any external sign-in libraries, parse them here with the utility functions provided.
}
- Sie übergeben die erforderlichen Informationen an getCredential(). Damit werden die Optionen in der unteren Tabelle anhand der Liste der Anmeldedatenoptionen und eines Aktivitätskontexts in diesem Kontext dargestellt.
- Sobald die Anforderung erfolgreich war, wird am unteren Rand des Bildschirms eine Liste mit allen erstellten Anmeldedaten für das verknüpfte Konto angezeigt.
- Nutzer können jetzt ihre Identität über biometrische Verfahren, eine Displaysperre usw. bestätigen, um die ausgewählten Anmeldedaten zu authentifizieren.
- Sie setzen das Flag „setSignedInThroughPasskeys“ als „true“, um darauf hinzuweisen, dass Sie sich über Passkeys anmelden. Ansonsten ist die Variable "false".
- Sie verarbeiten die Sichtbarkeit der gerenderten Ansichten und die Ausnahmen, wenn die Anfrage aus irgendeinem Grund fehlschlägt oder nicht erfolgreich ist. Hier werden die Fehlermeldungen protokolliert und in einem Fehlerdialogfeld in der App angezeigt. Die vollständigen Fehlerprotokolle können über Android Studio oder den Befehl „adb debug“ angezeigt werden.
- Abschließend müssen Sie den Registrierungsprozess abschließen, indem Sie die Anmeldedaten des öffentlichen Schlüssels an den Server senden und dem Nutzer Zugriff gewähren. Die App empfängt ein Anmeldedatenobjekt mit einem öffentlichen Schlüssel, den Sie zur Authentifizierung über den Passkey an den Server senden können.
Hier haben wir einen simulierten Server verwendet. Also geben wir einfach „true“ zurück, um anzugeben, dass der Server den öffentlichen Schlüssel validiert hat.
Suchen Sie in der Methode signInWithSavedCredentials
() nach einem relevanten Kommentar und ersetzen Sie ihn durch den folgenden Code:
SignInFragment.kt
//TODO : complete the authentication process after validating the public key credential to your server and let the user in.
data?.let {
sendSignInResponseToServer()
listener.showHome()
}
- sendSigninResponseToServer() gibt "true" zurück, um anzugeben, dass der (Beispiel-)Server den öffentlichen Schlüssel für die zukünftige Verwendung validiert hat.
- Nach der Anmeldung leiten Sie den Nutzer zum Startbildschirm weiter.
Das folgende Code-Snippet enthält ein PublicKeyCredential
-Beispielobjekt:
{
"id": String
"rawId": String
"type": "public-key",
"response": {
"clientDataJSON": String
"authenticatorData": String
"signature": String
"userHandle": String
}
}
Die folgende Tabelle ist nicht vollständig, enthält aber die wichtigen Parameter im PublicKeyCredential
-Objekt:
Parameter | Textzeilen |
Die Base64URL-codierte ID der authentifizierten Passkey-Anmeldedaten. | |
Eine | |
Ein | |
Ein | |
Ein | |
Ein |
Starte die App und melde dich an -> Melde dich mit Passkeys/gespeichertem Passwort an und versuche, dich mit gespeicherten Anmeldedaten anzumelden.
Ausprobieren
Sie haben in Ihrer Android-App die Erstellung von Passkeys, das Speichern von Passwörtern im Anmeldedaten-Manager und die Authentifizierung mithilfe von Passkeys oder gespeicherten Passwörtern mithilfe der Credential Manager API implementiert.
6. Glückwunsch!
Du hast dieses Codelab abgeschlossen. Die endgültige Auflösung finden Sie unter https://github.com/android/identity-samples/tree/main/CredentialManager.
Wenn Sie Fragen haben, können Sie diese auf StackOverflow mit einem passkey
-Tag stellen.