1. Einführung
Was ist die FIDO2 API?
Mit der FIDO2 API können Android-Anwendungen zur Authentifizierung von Nutzern starke, attestierte, auf öffentliche Schlüssel basierende Anmeldedaten erstellen und verwenden. Die API bietet eine Implementierung des WebAuthn-Clients, die die Verwendung von BLE-, NFC- und USB-Roaming-Authentifikatoren (Sicherheitsschlüsseln) sowie eines Plattformauthentifizierungsgeräts unterstützt, mit dem sich der Nutzer mit seinem Fingerabdruck oder seiner Displaysperre authentifizieren kann.
Das werden Sie erstellen...
In diesem Codelab entwickeln Sie eine Android-App mit einer einfachen Funktion zur erneuten Authentifizierung mithilfe des Fingerabdrucksensors. „Erneute Authentifizierung“ Ein Nutzer meldet sich in einer App an und authentifiziert sich noch einmal, wenn er zu deiner App zurückkehrt oder auf einen wichtigen Bereich deiner App zugreift. Der letzte Fall wird auch als „Step-up-Authentifizierung“ bezeichnet.
Lerninhalte
Sie erfahren, wie Sie die Android FIDO2 API und Optionen aufrufen, die Sie für verschiedene Anlässe bereitstellen können. Außerdem lernen Sie Best Practices für die erneute Autorisierung kennen.
Das brauchen Sie...
- Android-Gerät mit Fingerabdrucksensor (auch ohne Fingerabdrucksensor bietet die Displaysperre eine gleichwertige Funktion zur Nutzerbestätigung)
- Android 7.0 oder höher mit den neuesten Updates Registrieren Sie einen Fingerabdruck (oder eine Displaysperre).
2. Einrichtung
Repository klonen
GitHub-Repository ansehen
https://github.com/android/codelab-fido2
$ git clone https://github.com/android/codelab-fido2.git
Was werden wir implementieren?
- Nutzer dürfen einen „Plattform-Authenticator zur Nutzerbestätigung“ registrieren Das Android-Smartphone mit Fingerabdrucksensor fungiert dann als solches.
- Nutzer dürfen sich mit ihrem Fingerabdruck noch einmal in der App authentifizieren.
Hier können Sie sich eine Vorschau ansehen.
Codelab-Projekt starten
Die fertige Anwendung sendet Anfragen an einen Server unter https://webauthn-codelab.glitch.me. Sie können dort die Webversion derselben App ausprobieren.
Sie arbeiten an Ihrer eigenen Version der App.
- Rufen Sie unter https://glitch.com/edit/#!/webauthn-codelab die Bearbeitungsseite der Website auf.
- Suche nach „Remix to Edit“ (Remix zum Bearbeiten) oben rechts. Durch Drücken der Taste kannst du „verzweigen“ und fahren Sie mit Ihrer eigenen Version zusammen mit einer neuen Projekt-URL fort.
- Kopieren Sie den Projektnamen oben links. Sie können ihn beliebig ändern.
- Füge ihn in den Abschnitt
HOSTNAME
der Datei.env
im Glitch ein.
3. Ihre App und eine Website mit Digital Asset Links verknüpfen
Wenn Sie die FIDO2 API in einer Android-App verwenden möchten, verknüpfen Sie sie mit einer Website und teilen Sie die Anmeldedaten zwischen ihnen. Verwende dazu Digital Asset Links. Sie können Verknüpfungen deklarieren, indem Sie eine Digital Asset Links-JSON-Datei auf Ihrer Website hosten und dem Manifest Ihrer App einen Link zur Digital Asset Link-Datei hinzufügen.
.well-known/assetlinks.json
in Ihrer Domain hosten
Sie können eine Verknüpfung zwischen Ihrer App und der Website definieren, indem Sie eine JSON-Datei erstellen und unter .well-known/assetlinks.json
ablegen. Zum Glück gibt es einen Servercode, mit dem die assetlinks.json
-Datei automatisch angezeigt wird. Dazu müssen Sie lediglich die folgenden Umgebungsparameter in die .env
-Datei für eine Störung einfügen:
ANDROID_PACKAGENAME
: Paketname Ihrer App (com.beispiel.android.fido2)ANDROID_SHA256HASH
: SHA256-Hash Ihres Signaturzertifikats
Verwenden Sie den folgenden Befehl, um den SHA256-Hash Ihres Signaturzertifikats für Entwickler abzurufen. Das Standardpasswort des Keystores für die Fehlerbehebung lautet „android“.
$ keytool -exportcert -list -v -alias androiddebugkey -keystore ~/.android/debug.keystore
Wenn Sie auf https://<your-project-name>.glitch.me/.well-known/assetlinks.json
zugreifen , sollte ein JSON-String wie der folgende angezeigt werden:
[{
"relation": ["delegate_permission/common.handle_all_urls", "delegate_permission/common.get_login_creds"],
"target": {
"namespace": "web",
"site": "https://<your-project-name>.glitch.me"
}
}, {
"relation": ["delegate_permission/common.handle_all_urls", "delegate_permission/common.get_login_creds"],
"target": {
"namespace": "android_app",
"package_name": "com.example.android.fido2",
"sha256_cert_fingerprints": ["DE:AD:BE:EF:..."]
}
}]
Projekt in Android Studio öffnen
Klicken Sie auf "Vorhandenes Android Studio-Projekt öffnen". auf dem Begrüßungsbildschirm von Android Studio.
Wählen Sie die Option „Android“ aus. Ordner im Repository auszuchecken.
App mit deinem Remix verknüpfen
Datei „gradle.properties
“ öffnen. Ändere unten in der Datei die Host-URL in den Glitch-Remix, den du gerade erstellt hast.
// ...
# The URL of the server
host=https://<your-project-name>.glitch.me
Jetzt sollte deine Digital Asset Links-Konfiguration abgeschlossen sein.
4. So funktioniert die App jetzt
Sehen wir uns zunächst an, wie die App jetzt funktioniert. Achten Sie darauf, im Kombinationsfeld für die Ausführungskonfiguration app-start auszuwählen. Klicken Sie auf „Ausführen“. (das grüne Dreieck neben dem Kombinationsfeld) aus, um die App auf Ihrem verbundenen Android-Gerät zu starten.
Wenn du die App startest, erscheint der Bildschirm, auf dem du deinen Nutzernamen eingeben kannst. Das ist UsernameFragment
. Zu Demonstrationszwecken akzeptieren die App und der Server einen beliebigen Nutzernamen. Geben Sie einfach etwas ein und klicken Sie auf „Weiter“.
Der nächste Bildschirm, den du siehst, ist AuthFragment
. Dort kann sich der Nutzer mit einem Passwort anmelden. Später werden wir hier eine Funktion zur Anmeldung mit FIDO2 hinzufügen. Zu Demonstrationszwecken akzeptieren die App und der Server ein beliebiges Passwort. Gib einfach einen Text ein und klicke auf „Anmelden“.
Dies ist der letzte Bildschirm dieser App, HomeFragment
. Aktuell sehen Sie hier nur eine leere Liste mit Anmeldedaten. Drücken Sie auf „Erneute Authentifizierung“. führt Sie zurück zu AuthFragment
. Tippe auf „Abmelden“. führt Sie zurück zu UsernameFragment
. Die unverankerte Aktionsschaltfläche mit „+“ ändert sich zwar noch nichts, aber es wird die Registrierung eines
sobald Sie den FIDO2-Registrierungsvorgang implementiert haben.
Bevor Sie mit dem Programmieren beginnen, sollten Sie Folgendes beachten: Drücke in Android Studio auf "TODO". . Daraufhin wird eine Liste aller TODOs in diesem Codelab angezeigt. Wir beginnen mit dem ersten TODO im nächsten Abschnitt.
5. Anmeldedaten mit einem Fingerabdruck registrieren
Um die Authentifizierung mithilfe eines Fingerabdrucks zu aktivieren, müssen Sie zuerst einen Berechtigungsnachweis registrieren, der von einem Nutzer generiert wurde, der die Authentifizierung der Plattform bestätigt. Dabei handelt es sich um einen in das Gerät eingebetteten Authenticator, der den Nutzer mithilfe biometrischer Verfahren, z. B. eines Fingerabdrucksensors, verifiziert.
Wie im vorherigen Abschnitt gesehen, hat die unverankerte Aktionsschaltfläche jetzt keine Funktion. Sehen wir uns an, wie wir einen neuen Berechtigungsnachweis registrieren können.
Server-API aufrufen: /auth/registerRequest
Öffnen Sie AuthRepository.kt
und suchen Sie nach TODO(1).
Hier ist registerRequest
die Methode, die aufgerufen wird, wenn die UAS gedrückt wird. Diese Methode soll die Server-API /auth/registerRequest
aufrufen. Die API gibt ein ApiResult
mit allen PublicKeyCredentialCreationOptions
zurück, die der Client zum Generieren neuer Anmeldedaten benötigt.
Wir können dann getRegisterPendingIntent
mit den Optionen aufrufen. Diese FIDO2-API gibt einen Android PendingIntent zurück, um einen Fingerabdruckdialog zu öffnen und neue Anmeldedaten zu generieren. Dieser PendingIntent kann an den Aufrufer zurückgegeben werden.
Die Methode sieht dann so aus:
suspend fun registerRequest(): PendingIntent? {
fido2ApiClient?.let { client ->
try {
val sessionId = dataStore.read(SESSION_ID)!!
when (val apiResult = api.registerRequest(sessionId)) {
ApiResult.SignedOutFromServer -> forceSignOut()
is ApiResult.Success -> {
if (apiResult.sessionId != null) {
dataStore.edit { prefs ->
prefs[SESSION_ID] = apiResult.sessionId
}
}
val task = client.getRegisterPendingIntent(apiResult.data)
return task.await()
}
}
} catch (e: Exception) {
Log.e(TAG, "Cannot call registerRequest", e)
}
}
return null
}
Fingerabdruck-Dialogfeld für die Registrierung öffnen
Öffnen Sie HomeFragment.kt
und suchen Sie nach TODO(2).
Hier wird der Intent von der AuthRepository
über die UI zurückgegeben. Hier verwenden wir das Mitglied createCredentialIntentLauncher
, um den PendingIntent zu starten, den wir als Ergebnis des vorherigen Schritts erhalten haben. Dadurch wird ein Dialogfeld zum Erstellen von Anmeldedaten geöffnet.
binding.add.setOnClickListener {
lifecycleScope.launch {
val intent = viewModel.registerRequest()
if (intent != null) {
createCredentialIntentLauncher.launch(
IntentSenderRequest.Builder(intent).build()
)
}
}
}
ActivityResult mit den neuen Anmeldedaten erhalten
Öffnen Sie HomeFragment.kt
und suchen Sie nach TODO(3).
Diese handleCreateCredentialResult
-Methode wird aufgerufen, nachdem das Fingerabdruck-Dialogfeld geschlossen wurde. Wenn ein Berechtigungsnachweis erfolgreich generiert wurde, enthält das Mitglied data
von ActivityResult diese Informationen.
Zuerst müssen wir PublicKeyCredential aus data
extrahieren. Der Daten-Intent hat ein zusätzliches Feld aus Byte-Array mit dem Schlüssel Fido.FIDO2_KEY_CREDENTIAL_EXTRA
. Sie können die statische Methode deserializeFromBytes
in PublicKeyCredential
verwenden, um das Byte-Array in ein PublicKeyCredential
-Objekt umzuwandeln.
Prüfen Sie als Nächstes, ob das response
-Mitglied dieses Anmeldedatenobjekts ein AuthenticationErrorResponse
-Element ist. Wenn dies der Fall ist, ist beim Generieren des Berechtigungsnachweises ein Fehler aufgetreten. Andernfalls können wir
Anmeldedaten an unser Back-End senden.
Die fertige Methode sieht so aus:
private fun handleCreateCredentialResult(activityResult: ActivityResult) {
val bytes = activityResult.data?.getByteArrayExtra(Fido.FIDO2_KEY_CREDENTIAL_EXTRA)
when {
activityResult.resultCode != Activity.RESULT_OK ->
Toast.makeText(requireContext(), R.string.cancelled, Toast.LENGTH_LONG).show()
bytes == null ->
Toast.makeText(requireContext(), R.string.credential_error, Toast.LENGTH_LONG)
.show()
else -> {
val credential = PublicKeyCredential.deserializeFromBytes(bytes)
val response = credential.response
if (response is AuthenticatorErrorResponse) {
Toast.makeText(requireContext(), response.errorMessage, Toast.LENGTH_LONG)
.show()
} else {
viewModel.registerResponse(credential)
}
}
}
}
Server-API aufrufen: /auth/registerResponse
Öffnen Sie AuthRepository.kt
und suchen Sie nach TODO(4).
Die Methode registerResponse
wird aufgerufen, nachdem die Benutzeroberfläche erfolgreich neue Anmeldedaten generiert hat und diese an den Server zurückgesendet werden sollen.
Das Objekt PublicKeyCredential
enthält Informationen zu den neu generierten Anmeldedaten. Wir möchten uns nun die ID unseres lokalen Schlüssels merken, damit wir ihn von anderen Schlüsseln unterscheiden können, die auf dem Server registriert sind. Fügen Sie im PublicKeyCredential
-Objekt das Attribut rawId
mit toBase64
in eine lokale Stringvariable ein.
Jetzt können die Informationen an den Server gesendet werden. Verwenden Sie api.registerResponse
, um die Server-API aufzurufen und die Antwort zurückzusenden. Der zurückgegebene Wert enthält eine Liste aller auf dem Server registrierten Anmeldedaten, einschließlich des neuen.
Schließlich können wir die Ergebnisse in DataStore
speichern. Die Liste der Anmeldedaten sollte mit dem Schlüssel CREDENTIALS
als StringSet
gespeichert werden. Sie können toStringSet
verwenden, um die Liste der Anmeldedaten in eine StringSet
umzuwandeln.
Außerdem speichern wir die Anmeldedaten-ID mit dem Schlüssel LOCAL_CREDENTIAL_ID
.
suspend fun registerResponse(credential: PublicKeyCredential) {
try {
val sessionId = dataStore.read(SESSION_ID)!!
val credentialId = credential.rawId.toBase64()
when (val result = api.registerResponse(sessionId, credential)) {
ApiResult.SignedOutFromServer -> forceSignOut()
is ApiResult.Success -> {
dataStore.edit { prefs ->
result.sessionId?.let { prefs[SESSION_ID] = it }
prefs[CREDENTIALS] = result.data.toStringSet()
prefs[LOCAL_CREDENTIAL_ID] = credentialId
}
}
}
} catch (e: ApiException) {
Log.e(TAG, "Cannot call registerResponse", e)
}
}
Führen Sie die App aus. Sie können dann auf die UAS klicken und einen neuen Berechtigungsnachweis registrieren.
6. Nutzer mit einem Fingerabdruck authentifizieren
Jetzt sind in der App und auf dem Server Anmeldedaten registriert. Jetzt können wir es verwenden, damit sich der Nutzer anmelden kann. Wir fügen AuthFragment
eine Anmeldefunktion mit Fingerabdruck hinzu. Sobald ein Nutzer darauf landet, wird ein Fingerabdruck-Dialogfeld angezeigt. Wenn die Authentifizierung erfolgreich ist, wird der Nutzer zu HomeFragment
weitergeleitet.
Server-API aufrufen: /auth/signinRequest
Öffnen Sie AuthRepository.kt
und suchen Sie nach TODO(5).
Diese signinRequest
-Methode wird beim Öffnen von AuthFragment
aufgerufen. Hier möchten wir den Server anfordern und prüfen, ob sich der Nutzer mit FIDO2 anmelden kann.
Zuerst müssen wir PublicKeyCredentialRequestOptions
vom Server abrufen. Verwenden Sie api.signInRequest
, um die Server-API aufzurufen. Die zurückgegebene ApiResult
enthält PublicKeyCredentialRequestOptions
.
Mit der PublicKeyCredentialRequestOptions
können wir die getSignIntent
der FIDO2 API verwenden, um einen PendingIntent zu erstellen, mit dem das Fingerabdruck-Dialogfeld geöffnet wird.
Schließlich können wir den PendingIntent wieder an die Benutzeroberfläche zurückgeben.
suspend fun signinRequest(): PendingIntent? {
fido2ApiClient?.let { client ->
val sessionId = dataStore.read(SESSION_ID)!!
val credentialId = dataStore.read(LOCAL_CREDENTIAL_ID)
if (credentialId != null) {
when (val apiResult = api.signinRequest(sessionId, credentialId)) {
ApiResult.SignedOutFromServer -> forceSignOut()
is ApiResult.Success -> {
val task = client.getSignPendingIntent(apiResult.data)
return task.await()
}
}
}
}
return null
}
Fingerabdruck-Dialogfeld zur Assertion öffnen
Öffnen Sie AuthFragment.kt
und suchen Sie nach TODO(6).
Das funktioniert im Grunde genauso wie bei der Registrierung. Wir können das Fingerabdruck-Dialogfeld mit dem signIntentLauncher
-Mitglied öffnen.
viewLifecycleOwner.lifecycleScope.launchWhenStarted {
launch {
viewModel.signinRequests.collect { intent ->
signIntentLauncher.launch(
IntentSenderRequest.Builder(intent).build()
)
}
}
launch {
...
}
}
ActivityResult verarbeiten
Öffnen Sie AuthFragment.kt und suchen Sie nach TODO(7).
Auch dies ist dasselbe wie bei der Registrierung. Wir können die PublicKeyCredential
extrahieren, nach einem Fehler suchen und sie an ViewModel übergeben.
private fun handleSignResult(activityResult: ActivityResult) {
val bytes = activityResult.data?.getByteArrayExtra(Fido.FIDO2_KEY_CREDENTIAL_EXTRA)
when {
activityResult.resultCode != Activity.RESULT_OK ->
Toast.makeText(requireContext(), R.string.cancelled, Toast.LENGTH_SHORT).show()
bytes == null ->
Toast.makeText(requireContext(), R.string.auth_error, Toast.LENGTH_SHORT)
.show()
else -> {
val credential = PublicKeyCredential.deserializeFromBytes(bytes)
val response = credential.response
if (response is AuthenticatorErrorResponse) {
Toast.makeText(requireContext(), response.errorMessage, Toast.LENGTH_SHORT)
.show()
} else {
viewModel.signinResponse(credential)
}
}
}
}
Server-API aufrufen: /auth/signinResponse
Öffnen Sie AuthRepository.kt
und suchen Sie nach TODO(8).
Das Objekt „PublicKeyCredential“ enthält die Ausweisdokument-ID keyHandle
. Genau wie beim Registrierungsvorgang speichern wir dies in einer lokalen Zeichenfolgenvariablen, damit wir es später speichern können.
Jetzt kann die Server-API mit api.signinResponse
aufgerufen werden. Der zurückgegebene Wert enthält eine Liste von Anmeldedaten.
Zu diesem Zeitpunkt ist die Anmeldung erfolgreich. Wir müssen alle Ergebnisse in DataStore
speichern. Die Liste der Anmeldedaten sollte als StringSet mit dem Schlüssel CREDENTIALS
gespeichert werden. Die oben gespeicherte lokale Ausweisdokument-ID sollte als String mit dem Schlüssel LOCAL_CREDENTIAL_ID
gespeichert werden.
Schließlich müssen wir den Anmeldestatus aktualisieren, damit die Benutzeroberfläche den Nutzer zum HomeFragment weiterleiten kann. Dazu wird ein SignInState.SignedIn
-Objekt mit dem Namen signInStateMutable
an den SharedFlow ausgegeben. Außerdem möchten wir refreshCredentials
aufrufen, um die Anmeldedaten des Nutzers abzurufen, damit sie in der UI aufgelistet werden.
suspend fun signinResponse(credential: PublicKeyCredential) {
try {
val username = dataStore.read(USERNAME)!!
val sessionId = dataStore.read(SESSION_ID)!!
val credentialId = credential.rawId.toBase64()
when (val result = api.signinResponse(sessionId, credential)) {
ApiResult.SignedOutFromServer -> forceSignOut()
is ApiResult.Success -> {
dataStore.edit { prefs ->
result.sessionId?.let { prefs[SESSION_ID] = it }
prefs[CREDENTIALS] = result.data.toStringSet()
prefs[LOCAL_CREDENTIAL_ID] = credentialId
}
signInStateMutable.emit(SignInState.SignedIn(username))
refreshCredentials()
}
}
} catch (e: ApiException) {
Log.e(TAG, "Cannot call registerResponse", e)
}
}
Führen Sie die App aus und klicken Sie auf „Erneute Autorisierung“. um AuthFragment
zu öffnen. Es sollte nun ein Fingerabdruck-Dialogfeld angezeigt werden, in dem Sie aufgefordert werden, sich mit Ihrem Fingerabdruck anzumelden.
Glückwunsch! Sie wissen jetzt, wie Sie die FIDO2 API unter Android für die Registrierung und Anmeldung verwenden.
7. Glückwunsch!
Sie haben das Codelab Ihre erste Android FIDO2 API erfolgreich abgeschlossen.
Das haben Sie gelernt
- So registrieren Sie Anmeldedaten mit einem Plattform-Authenticator, der den Nutzer bestätigt.
- Nutzer mit einem registrierten Authenticator authentifizieren
- Verfügbare Optionen zum Registrieren einer neuen Authentifizierungs-App.
- UX-Best Practices für die erneute Authentifizierung mit einem biometrischen Sensor.
Nächster Schritt
- Hier erfahren Sie, wie Sie auf einer Website ein ähnliches Erlebnis schaffen.
Probieren Sie das Codelab Your first WebAuthn (Ihre erste WebAuthn-Version) aus.
Ressourcen
- WebAuthn-Spezifikation
- Einführung in die WebAuthn API
- FIDO WebAuthn-Workshop
- WebAuthn-Leitfaden: DUOSEC
Vielen Dank an Yuriy Ackermann von der FIDO Alliance für die Hilfe.