Blockhaus

Viele Nutzer verwalten ihre Anmeldedaten weiterhin selbst, wenn sie ein neues Android-Gerät einrichten. Dieser manuelle Prozess kann schwierig werden und führt häufig zu einer schlechten Nutzererfahrung. Die Block Store API, eine Bibliothek, die auf Google Play-Diensten basiert, bietet eine Möglichkeit, Nutzeranmeldedaten zu speichern, ohne die Komplexität oder das Sicherheitsrisiken, die mit dem Speichern von Nutzerpasswörtern verbunden ist.

Mit der Block Store API kann Ihre Anwendung Daten speichern, die sie später abrufen kann, um Nutzer auf einem neuen Gerät noch einmal zu authentifizieren. Dies sorgt für eine nahtlosere Nutzung, da sie keinen Anmeldebildschirm sehen müssen, wenn sie deine App zum ersten Mal auf dem neuen Gerät starten.

Die Verwendung von Block Store bietet unter anderem folgende Vorteile:

  • Verschlüsselte Anmeldedatenspeicherlösung für Entwickler Anmeldedaten werden nach Möglichkeit Ende-zu-Ende-verschlüsselt.
  • Speichern Sie Tokens anstelle von Nutzernamen und Passwörtern.
  • Die Anmeldung wird reibungsloser gestaltet.
  • Ersparen Sie Nutzern die Verwaltung komplexer Passwörter.
  • Google überprüft die Identität des Nutzers.

Hinweis

Führen Sie die Schritte in den folgenden Abschnitten aus, um Ihre App vorzubereiten.

Eigene App konfigurieren

Fügen Sie in die Datei build.gradle auf Projektebene das Maven-Repository von Google in die Abschnitte buildscript und allprojects ein:

buildscript {
  repositories {
    google()
    mavenCentral()
  }
}

allprojects {
  repositories {
    google()
    mavenCentral()
  }
}

Fügen Sie die Abhängigkeit der Google Play-Dienste für die Block Store API in die Gradle-Build-Datei Ihres Moduls ein. Diese lautet normalerweise app/build.gradle:

dependencies {
  implementation 'com.google.android.gms:play-services-auth-blockstore:16.2.0'
}

Funktionsweise

Block Store ermöglicht es Entwicklern, bis zu 16-Byte-Arrays zu speichern und wiederherzustellen. So können Sie wichtige Informationen zur aktuellen Nutzersitzung speichern und diese Informationen nach Belieben speichern. Diese Daten können mit Ende-zu-Ende-Verschlüsselung geschützt werden und die Infrastruktur, die Block Store unterstützt, baut auf der Backup and Restore-Infrastruktur auf.

In diesem Leitfaden wird der Anwendungsfall des Speicherns eines Nutzertokens im Block Store behandelt. Die folgenden Schritte umreißen die Funktionsweise einer App, die den Block Store verwendet:

  1. Während des Authentifizierungsvorgangs deiner App oder jederzeit danach kannst du das Authentifizierungstoken des Nutzers zum späteren Abrufen im Blockspeicher speichern.
  2. Das Token wird lokal gespeichert und kann nach Möglichkeit mit Ende-zu-Ende-Verschlüsselung in der Cloud gesichert werden.
  3. Daten werden übertragen, wenn der Nutzer einen Wiederherstellungsablauf auf einem neuen Gerät initiiert.
  4. Wenn der Nutzer Ihre App während des Wiederherstellungsvorgangs wiederhergestellt, kann sie das gespeicherte Token aus dem Block Store auf dem neuen Gerät abrufen.

Token wird gespeichert

Wenn sich ein Nutzer bei Ihrer App anmeldet, können Sie das Authentifizierungstoken, das Sie für diesen Nutzer generieren, in „Store blockieren“ speichern. Sie können dieses Token mit einem eindeutigen Schlüsselpaarwert mit maximal 4 KB pro Eintrag speichern. Rufen Sie zum Speichern des Tokens setBytes() und setKey() auf einer Instanz von StoreBytesData.Builder auf, um die Anmeldedaten des Nutzers auf dem Quellgerät zu speichern. Nachdem Sie das Token mit Block Store gespeichert haben, wird es verschlüsselt und lokal auf dem Gerät gespeichert.

Das folgende Beispiel zeigt, wie das Authentifizierungstoken auf dem lokalen Gerät gespeichert wird:

Java

  BlockstoreClient client = Blockstore.getClient(this);
  byte[] bytes1 = new byte[] { 1, 2, 3, 4 };  // Store one data block.
  String key1 = "com.example.app.key1";
  StoreBytesData storeRequest1 = StoreBytesData.Builder()
          .setBytes(bytes1)
          // Call this method to set the key value pair the data should be associated with.
          .setKeys(Arrays.asList(key1))
          .build();
  client.storeBytes(storeRequest1)
    .addOnSuccessListener(result -> Log.d(TAG, "stored " + result + " bytes"))
    .addOnFailureListener(e -> Log.e(TAG, "Failed to store bytes", e));

Kotlin

  val client = Blockstore.getClient(this)

  val bytes1 = byteArrayOf(1, 2, 3, 4) // Store one data block.
  val key1 = "com.example.app.key1"
  val storeRequest1 = StoreBytesData.Builder()
    .setBytes(bytes1) // Call this method to set the key value with which the data should be associated with.
    .setKeys(Arrays.asList(key1))
    .build()
  client.storeBytes(storeRequest1)
    .addOnSuccessListener { result: Int ->
      Log.d(TAG,
            "Stored $result bytes")
    }
    .addOnFailureListener { e ->
      Log.e(TAG, "Failed to store bytes", e)
    }

Standardtoken verwenden

Daten, die mit StoreBytes ohne Schlüssel gespeichert werden, verwenden den Standardschlüssel BlockstoreClient.DEFAULT_BYTES_DATA_KEY.

Java

  BlockstoreClient client = Blockstore.getClient(this);
  // The default key BlockstoreClient.DEFAULT_BYTES_DATA_KEY.
  byte[] bytes = new byte[] { 9, 10 };
  StoreBytesData storeRequest = StoreBytesData.Builder()
          .setBytes(bytes)
          .build();
  client.storeBytes(storeRequest)
    .addOnSuccessListener(result -> Log.d(TAG, "stored " + result + " bytes"))
    .addOnFailureListener(e -> Log.e(TAG, "Failed to store bytes", e));

Kotlin

  val client = Blockstore.getClient(this);
  // the default key BlockstoreClient.DEFAULT_BYTES_DATA_KEY.
  val bytes = byteArrayOf(1, 2, 3, 4)
  val storeRequest = StoreBytesData.Builder()
    .setBytes(bytes)
    .build();
  client.storeBytes(storeRequest)
    .addOnSuccessListener { result: Int ->
      Log.d(TAG,
            "stored $result bytes")
    }
    .addOnFailureListener { e ->
      Log.e(TAG, "Failed to store bytes", e)
    }

Token abrufen

Wenn ein Nutzer später den Wiederherstellungsablauf auf einem neuen Gerät durchläuft, überprüfen die Google Play-Dienste den Nutzer zuerst und rufen dann deine Block Store-Daten ab. Der Nutzer hat bereits zugestimmt, Ihre App-Daten im Rahmen des Wiederherstellungsablaufs wiederherzustellen, sodass keine zusätzlichen Einwilligungen erforderlich sind. Wenn der Nutzer Ihre App öffnet, können Sie Ihr Token durch Aufrufen von retrieveBytes() vom Block Store anfordern. Mit dem abgerufenen Token kann der Nutzer dann auf dem neuen Gerät angemeldet bleiben.

Das folgende Beispiel zeigt, wie mehrere Tokens basierend auf bestimmten Schlüsseln abgerufen werden.

Java

BlockstoreClient client = Blockstore.getClient(this);

// Retrieve data associated with certain keys.
String key1 = "com.example.app.key1";
String key2 = "com.example.app.key2";
String key3 = BlockstoreClient.DEFAULT_BYTES_DATA_KEY; // Used to retrieve data stored without a key

List requestedKeys = Arrays.asList(key1, key2, key3); // Add keys to array
RetrieveBytesRequest retrieveRequest = new RetrieveBytesRequest.Builder()
    .setKeys(requestedKeys)
    .build();

client.retrieveBytes(retrieveRequest)
    .addOnSuccessListener(
        result -> {
          Map blockstoreDataMap = result.getBlockstoreDataMap();
          for (Map.Entry entry : blockstoreDataMap.entrySet()) {
            Log.d(TAG, String.format(
                "Retrieved bytes %s associated with key %s.",
                new String(entry.getValue().getBytes()), entry.getKey()));
          }
        })
    .addOnFailureListener(e -> Log.e(TAG, "Failed to store bytes", e));

Kotlin

val client = Blockstore.getClient(this)

// Retrieve data associated with certain keys.
val key1 = "com.example.app.key1"
val key2 = "com.example.app.key2"
val key3 = BlockstoreClient.DEFAULT_BYTES_DATA_KEY // Used to retrieve data stored without a key

val requestedKeys = Arrays.asList(key1, key2, key3) // Add keys to array

val retrieveRequest = RetrieveBytesRequest.Builder()
  .setKeys(requestedKeys)
  .build()

client.retrieveBytes(retrieveRequest)
  .addOnSuccessListener { result: RetrieveBytesResponse ->
    val blockstoreDataMap =
      result.blockstoreDataMap
    for ((key, value) in blockstoreDataMap) {
      Log.d(ContentValues.TAG, String.format(
        "Retrieved bytes %s associated with key %s.",
        String(value.bytes), key))
    }
  }
  .addOnFailureListener { e: Exception? ->
    Log.e(ContentValues.TAG,
          "Failed to store bytes",
          e)
  }

Alle Tokens werden abgerufen.

Im Folgenden finden Sie ein Beispiel dafür, wie alle in BlockStore gespeicherten Tokens abgerufen werden.

Java

BlockstoreClient client = Blockstore.getClient(this)

// Retrieve all data.
RetrieveBytesRequest retrieveRequest = new RetrieveBytesRequest.Builder()
    .setRetrieveAll(true)
    .build();

client.retrieveBytes(retrieveRequest)
    .addOnSuccessListener(
        result -> {
          Map blockstoreDataMap = result.getBlockstoreDataMap();
          for (Map.Entry entry : blockstoreDataMap.entrySet()) {
            Log.d(TAG, String.format(
                "Retrieved bytes %s associated with key %s.",
                new String(entry.getValue().getBytes()), entry.getKey()));
          }
        })
    .addOnFailureListener(e -> Log.e(TAG, "Failed to store bytes", e));

Kotlin

val client = Blockstore.getClient(this)

val retrieveRequest = RetrieveBytesRequest.Builder()
  .setRetrieveAll(true)
  .build()

client.retrieveBytes(retrieveRequest)
  .addOnSuccessListener { result: RetrieveBytesResponse ->
    val blockstoreDataMap =
      result.blockstoreDataMap
    for ((key, value) in blockstoreDataMap) {
      Log.d(ContentValues.TAG, String.format(
        "Retrieved bytes %s associated with key %s.",
        String(value.bytes), key))
    }
  }
  .addOnFailureListener { e: Exception? ->
    Log.e(ContentValues.TAG,
          "Failed to store bytes",
          e)
  }

Im Folgenden finden Sie ein Beispiel für das Abrufen des Standardschlüssels.

Java

BlockStoreClient client = Blockstore.getClient(this);
RetrieveBytesRequest retrieveRequest = new RetrieveBytesRequest.Builder()
    .setKeys(Arrays.asList(BlockstoreClient.DEFAULT_BYTES_DATA_KEY))
    .build();
client.retrieveBytes(retrieveRequest);

Kotlin

val client = Blockstore.getClient(this)

val retrieveRequest = RetrieveBytesRequest.Builder()
  .setKeys(Arrays.asList(BlockstoreClient.DEFAULT_BYTES_DATA_KEY))
  .build()
client.retrieveBytes(retrieveRequest)

Tokens löschen

Das Löschen von Tokens aus BlockStore kann aus den folgenden Gründen erforderlich sein:

  • Der Nutzer durchläuft den User Flow zum Abmelden.
  • Das Token wurde widerrufen oder ist ungültig.

Ähnlich wie beim Abrufen von Tokens können Sie angeben, welche Tokens gelöscht werden müssen, indem Sie ein Array von Schlüsseln festlegen, die gelöscht werden müssen.

Im Folgenden finden Sie ein Beispiel für das Löschen bestimmter Schlüssel.

Java

BlockstoreClient client = Blockstore.getClient(this);

// Delete data associated with certain keys.
String key1 = "com.example.app.key1";
String key2 = "com.example.app.key2";
String key3 = BlockstoreClient.DEFAULT_BYTES_DATA_KEY; // Used to delete data stored without key

List requestedKeys = Arrays.asList(key1, key2, key3) // Add keys to array
DeleteBytesRequest deleteRequest = new DeleteBytesRequest.Builder()
      .setKeys(requestedKeys)
      .build();
client.deleteBytes(deleteRequest)

Kotlin

val client = Blockstore.getClient(this)

// Retrieve data associated with certain keys.
val key1 = "com.example.app.key1"
val key2 = "com.example.app.key2"
val key3 = BlockstoreClient.DEFAULT_BYTES_DATA_KEY // Used to retrieve data stored without a key

val requestedKeys = Arrays.asList(key1, key2, key3) // Add keys to array

val retrieveRequest = DeleteBytesRequest.Builder()
      .setKeys(requestedKeys)
      .build()

client.deleteBytes(retrieveRequest)

Alle Tokens löschen

Im folgenden Beispiel werden alle derzeit im BlockStore gespeicherten Tokens gelöscht:

Java

// Delete all data.
DeleteBytesRequest deleteAllRequest = new DeleteBytesRequest.Builder()
      .setDeleteAll(true)
      .build();
client.deleteBytes(deleteAllRequest)
.addOnSuccessListener(result -> Log.d(TAG, "Any data found and deleted? " + result));

Kotlin

  val deleteAllRequest = DeleteBytesRequest.Builder()
  .setDeleteAll(true)
  .build()
client.deleteBytes(deleteAllRequest)
  .addOnSuccessListener { result: Boolean ->
    Log.d(TAG,
          "Any data found and deleted? $result")
  }

Ende-zu-Ende-Verschlüsselung

Damit die Ende-zu-Ende-Verschlüsselung zur Verfügung gestellt werden kann, muss auf dem Gerät Android 9 oder höher installiert sein. Außerdem muss der Nutzer eine Displaysperre (PIN, Muster oder Passwort) für sein Gerät eingerichtet haben. Durch Aufrufen von isEndToEndEncryptionAvailable() können Sie prüfen, ob die Verschlüsselung auf dem Gerät verfügbar ist.

Das folgende Beispiel zeigt, wie Sie prüfen können, ob die Verschlüsselung während der Cloud-Sicherung verfügbar ist:

client.isEndToEndEncryptionAvailable()
        .addOnSuccessListener { result ->
          Log.d(TAG, "Will Block Store cloud backup be end-to-end encrypted? $result")
        }

Cloud-Sicherung aktivieren

Fügen Sie dem StoreBytesData-Objekt die Methode setShouldBackupToCloud() hinzu, um die Cloud-Sicherung zu aktivieren. Block Store sichert die gespeicherten Byte regelmäßig in der Cloud, wenn setShouldBackupToCloud() auf „true“ gesetzt ist.

Das folgende Beispiel zeigt, wie die Cloud-Sicherung nur dann aktiviert wird, wenn Cloud-Sicherungen mit Ende-zu-Ende-Verschlüsselung geschützt sind:

val client = Blockstore.getClient(this)
val storeBytesDataBuilder = StoreBytesData.Builder()
        .setBytes(/* BYTE_ARRAY */)

client.isEndToEndEncryptionAvailable()
        .addOnSuccessListener { isE2EEAvailable ->
          if (isE2EEAvailable) {
            storeBytesDataBuilder.setShouldBackupToCloud(true)
            Log.d(TAG, "E2EE is available, enable backing up bytes to the cloud.")

            client.storeBytes(storeBytesDataBuilder.build())
                .addOnSuccessListener { result ->
                  Log.d(TAG, "stored: ${result.getBytesStored()}")
                }.addOnFailureListener { e ->
                  Log.e(TAG, “Failed to store bytes”, e)
                }
          } else {
            Log.d(TAG, "E2EE is not available, only store bytes for D2D restore.")
          }
        }

Testanleitung

Verwenden Sie während der Entwicklung die folgenden Methoden, um die Wiederherstellungsabläufe zu testen.

Deinstallation/Neuinstallation auf demselben Gerät

Wenn der Nutzer Sicherungsdienste aktiviert, was unter Einstellungen > Google > Sicherung überprüft werden kann, bleiben die Daten des Blockspeichers während der Deinstallation/Neuinstallation der App erhalten.

So testen Sie:

  1. BlockStore API in Ihre Test-App einbinden
  2. Verwenden Sie die Test-App, um die BlockStore API zum Speichern Ihrer Daten aufzurufen.
  3. Deinstallieren Sie die Test-App und installieren Sie sie dann auf demselben Gerät neu.
  4. Verwenden Sie die Testanwendung, um die BlockStore API aufzurufen und Ihre Daten abzurufen.
  5. Prüfen Sie, ob die abgerufenen Byte mit denen übereinstimmen, die vor der Deinstallation gespeichert wurden.

Gerät zu Gerät

In den meisten Fällen muss dazu das Zielgerät auf die Werkseinstellungen zurückgesetzt werden. Anschließend können Sie mit dem Vorgang für die WLAN-Wiederherstellung unter Android oder der Wiederherstellung über das Google-Kabel (bei unterstützten Geräten) beginnen.

Cloud-Wiederherstellung

  1. Binde die Blockstore API in deine Test-App ein. Die Test-App muss an den Play Store gesendet werden.
  2. Verwenden Sie auf dem Quellgerät die Test-App, um die Blockstore API zum Speichern Ihrer Daten aufzurufen. Dabei sollte sollteBackUpToCloud auf „true“ gesetzt sein.
  3. Bei O-Geräten und höher können Sie manuell ein Block Store-Cloud-Back-up auslösen: Gehen Sie zu Einstellungen > Google > Sicherung und klicken Sie auf die Schaltfläche „Jetzt sichern“.
    1. So prüfen Sie, ob die Block Store-Cloud-Sicherung erfolgreich war:
      1. Suchen Sie nach Abschluss der Sicherung nach Logzeilen mit dem Tag „CloudSyncBpTkSvc“.
      2. Sie sollten Zeilen wie diese sehen: "......, CloudSyncBpTkSvc: Synchronisierungsergebnis: SUCCESS, ..., Uploadgröße: XXX Bytes ..."
    2. Nach einem Block Store-Cloud-Back-up gibt es eine Abkühlphase von 5 Minuten. Innerhalb dieser fünf Minuten wird durch Klicken auf die Schaltfläche „Jetzt sichern“ kein weiteres Block Store-Cloud-Back-up ausgelöst.
  4. Setzen Sie das Zielgerät auf die Werkseinstellungen zurück und führen Sie eine Cloud-Wiederherstellung durch. Wählen Sie diese Option aus, um Ihre Test-App während des Wiederherstellungsvorgangs wiederherzustellen. Weitere Informationen zu Cloud-Wiederherstellungsabläufen finden Sie unter Unterstützte Cloud-Wiederherstellungsabläufe.
  5. Verwenden Sie die Test-App auf dem Zielgerät, um die Blockstore API zum Abrufen Ihrer Daten aufzurufen.
  6. Prüfen Sie, ob die abgerufenen Byte mit denen übereinstimmen, die auf dem Quellgerät gespeichert wurden.

Geräteanforderungen

Ende-zu-Ende-Verschlüsselung

  • Die Ende-zu-Ende-Verschlüsselung wird auf Geräten mit Android 9 (API 29) und höher unterstützt.
  • Auf dem Gerät muss eine Displaysperre mit PIN, Muster oder Passwort eingerichtet sein, damit die Ende-zu-Ende-Verschlüsselung aktiviert ist und die Daten des Nutzers korrekt verschlüsselt werden.

Ablauf der Wiederherstellung von einem Gerät zum Gerät

Für die Wiederherstellung von Gerät zu Gerät benötigen Sie ein Quell- und ein Zielgerät. Das sind die beiden Geräte, auf denen Daten übertragen werden.

Quellgeräte müssen für die Sicherung Android 6 (API 23) oder höher ausführen.

Ausrichtung auf Geräte mit Android 9 (API-Version 29) und höher, um wiederherzustellen

Weitere Informationen zur Wiederherstellung von einem Gerät zum Gerät finden Sie hier.

Ablauf für Cloud-Sicherung und -Wiederherstellung

Für die Sicherung und Wiederherstellung in der Cloud sind ein Quell- und ein Zielgerät erforderlich.

Quellgeräte müssen für die Sicherung Android 6 (API 23) oder höher ausführen.

Zielgeräte werden je nach Anbieter unterstützt. Pixel kann diese Funktion ab Android 9 (API 29) nutzen. Auf allen anderen Geräten muss Android 12 (API 31) oder höher installiert sein.