Blockhaus

Viele Nutzer verwalten ihre eigenen Anmeldedaten, wenn sie ein neues Android-Gerät einrichten. Dieser manuelle Prozess kann schwierig werden und oft zu einer schlechten Nutzererfahrung führen. Die Block Store API, eine auf Google Play-Diensten basierende Bibliothek, möchte dieses Problem lösen, indem sie Apps die Möglichkeit bietet, Nutzeranmeldedaten zu speichern, ohne die Komplexität oder das Sicherheitsrisiko beim Speichern von Nutzerpasswörtern.

Mit der Block Store API kann Ihre App Daten speichern, die sie später abrufen kann, um Nutzer auf einem neuen Gerät erneut zu authentifizieren. Dies sorgt für eine nahtlosere Nutzung, da beim ersten Starten der App auf dem neuen Gerät kein Anmeldebildschirm angezeigt werden muss.

Die Nutzung des Block Store bietet unter anderem folgende Vorteile:

  • Verschlüsselte Lösung zum Speichern von Anmeldedaten für Entwickler Anmeldedaten werden nach Möglichkeit mit Ende-zu-Ende-Verschlüsselung geschützt.
  • Speichern Sie Tokens anstelle von Nutzernamen und Passwörtern.
  • Sorgen Sie für eine reibungslose Anmeldung.
  • Nutzer müssen keine komplizierten Passwörter verwalten.
  • 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 der Datei build.gradle auf Projektebene in den Abschnitten buildscript und allprojects das Maven-Repository von Google ein:

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

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

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

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

Funktionsweise

Block Store ermöglicht Entwicklern, bis zu 16-Byte-Arrays zu speichern und wiederherzustellen. Auf diese Weise können Sie wichtige Informationen zur aktuellen Nutzersitzung speichern und haben die Flexibilität, diese Informationen beliebig zu speichern. Diese Daten können mit Ende-zu-Ende-Verschlüsselung geschützt werden. Die Infrastruktur, die Block Store unterstützt, baut auf der Sicherungs- und Wiederherstellungsinfrastruktur auf.

In diesem Leitfaden wird der Anwendungsfall des Speicherns eines Nutzertokens im Block Store behandelt. Die folgenden Schritte beschreiben, wie eine App, die Block Store verwendet, funktioniert:

  1. Während des Authentifizierungsvorgangs Ihrer App oder zu einem späteren Zeitpunkt können Sie das Authentifizierungstoken des Nutzers im Block Store speichern, um es später abzurufen.
  2. Das Token wird lokal gespeichert und kann auch in der Cloud mit Ende-zu-Ende-Verschlüsselung gesichert werden, wenn dies möglich ist.
  3. Die Daten werden übertragen, wenn der Nutzer auf einem neuen Gerät eine Wiederherstellung startet.
  4. Wenn der Nutzer Ihre App während des Wiederherstellungsvorgangs wiederherstellt, kann Ihre App das gespeicherte Token aus dem Block Store auf dem neuen Gerät abrufen.

Token speichern

Wenn sich ein Nutzer in Ihrer App anmeldet, können Sie das Authentifizierungstoken, das Sie für diesen Nutzer generieren, im Block Store 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() in 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 Wiederherstellungsvorgang auf einem neuen Gerät durchläuft, überprüfen die Google Play-Dienste zuerst den Nutzer und rufen dann die Block Store-Daten ab. Der Nutzer hat bereits eingewilligt, Ihre App-Daten im Rahmen der Wiederherstellung wiederherzustellen. Daher sind keine zusätzlichen Einwilligungen erforderlich. Wenn der Nutzer deine App öffnet, kannst du dein Token aus dem Block Store anfordern, indem du retrieveBytes() aufrufst. Das abgerufene Token kann dann verwendet werden, damit der Nutzer auf dem neuen Gerät angemeldet bleibt.

Im folgenden Beispiel wird gezeigt, 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, wie Sie alle im BlockStore gespeicherten Tokens abrufen können.

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:

  • Die Nutzenden durchlaufen den User Flow für die Abmeldung.
  • 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.

Unten sehen 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 in 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

Für eine Ende-zu-Ende-Verschlüsselung muss auf dem Gerät Android 9 oder höher installiert sein und der Nutzer muss eine Displaysperre (PIN, Muster oder Passwort) für sein Gerät eingerichtet haben. Sie können prüfen, ob die Verschlüsselung auf dem Gerät verfügbar ist, indem Sie isEndToEndEncryptionAvailable() aufrufen.

Das folgende Beispiel zeigt, wie Sie prüfen können, ob die Verschlüsselung während einer Cloudsicherung 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 erstellt regelmäßig eine Sicherung der gespeicherten Byte in der Cloud, wenn setShouldBackupToCloud() auf „true“ gesetzt ist.

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

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.")
          }
        }

Testen

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

Gleiches Gerät deinstallieren/neu installieren

Wenn der Nutzer die Sicherungsdienste aktiviert (kann unter Einstellungen > Google > Sicherung aktiviert werden), werden Block Store-Daten für die gesamte Deinstallation/Neuinstallation der App beibehalten.

So testen Sie Folgendes:

  1. Binden Sie die BlockStore API in Ihre Test-App ein.
  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 anschließend auf demselben Gerät neu.
  4. Verwenden Sie die Test-App, um die BlockStore API aufzurufen und Ihre Daten abzurufen.
  5. Prüfen Sie, ob die abgerufenen Byte mit denen vor der Deinstallation identisch sind.

Gerät zu Gerät

In den meisten Fällen müssen Sie dazu das Zielgerät auf die Werkseinstellungen zurücksetzen. Anschließend können Sie den Vorgang für die kabellose Wiederherstellung von Android-Geräten oder die Google-Kabelwiederherstellung (für unterstützte Geräte) starten.

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 aufzurufen und Ihre Daten zu speichern. Der Wert sollte dabei auf „true“ gesetzt sein.
  3. Auf Geräten mit O und höher können Sie manuell eine Block Store-Cloudsicherung auslösen: Rufen Sie Einstellungen > Google > Sicherung auf und klicken Sie auf die Schaltfläche „Jetzt sichern“.
    1. So prüfen Sie, ob die Block Store-Cloudsicherung erfolgreich war:
      1. Suchen Sie nach Abschluss der Sicherung nach Logzeilen mit dem Tag „CloudSyncBpTkSvc“.
      2. Sie sollten folgende Zeilen sehen: "......, CloudSyncBpTkSvc: sync result: SUCCESS, ..., uploaded size: XXX bytes .."
    2. Nach einem Block Store-Cloud-Back-up folgt eine 5-minütige Wartezeit. Innerhalb dieser 5 Minuten wird durch Klicken auf die Schaltfläche „Jetzt sichern“ keine weitere Block Store-Cloudsicherung ausgelöst.
  4. Setzen Sie das Zielgerät auf die Werkseinstellungen zurück und führen Sie einen Cloud-Wiederherstellungsvorgang durch. Wählen Sie diese Option aus, um die Test-App während des Wiederherstellungsvorgangs wiederherzustellen. Weitere Informationen zu Cloud-Wiederherstellungsabläufen finden Sie unter Unterstützte Cloud-Wiederherstellungsabläufe.
  5. Verwenden Sie auf dem Zielgerät die Test-App, um die Blockstore API aufzurufen und Ihre Daten abzurufen.
  6. Prüfen Sie, ob die abgerufenen Byte mit denen auf dem Quellgerät übereinstimmen.

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 einer PIN, einem Muster oder einem Passwort eingerichtet sein, damit die Ende-zu-Ende-Verschlüsselung aktiviert wird und die Daten des Nutzers korrekt verschlüsselt werden.

Wiederherstellung von Gerät zu 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, die Daten übertragen.

Auf Quellgeräten muss für die Sicherung Android 6 (API 23) oder höher ausgeführt werden.

Ziel für Geräte mit Android 9 (API 29) und höher, damit eine Wiederherstellung möglich ist

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

Cloud-Sicherungs- und -Wiederherstellungsablauf

Zum Sichern und Wiederherstellen in der Cloud sind ein Quell- und ein Zielgerät erforderlich.

Auf Quellgeräten muss für die Sicherung Android 6 (API 23) oder höher ausgeführt werden.

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