Blockhaus

Viele Nutzer verwalten ihre eigenen Anmeldedaten, wenn sie ein neues Android-Gerät einrichten. Dieser manuelle Prozess kann eine Herausforderung darstellen und häufig zu einer schlechten Nutzererfahrung führen. Mit der Block Store API, einer auf Google Play-Diensten basierenden Bibliothek, lässt sich dieses Problem lösen. Sie bietet Apps die Möglichkeit, Nutzeranmeldedaten ohne die Komplexität oder das Sicherheitsrisiko zu speichern, die mit dem Speichern von Nutzerpasswörtern verbunden sind.

Mit der Block Store API kann Ihre Anwendung 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 Start der App auf dem neuen Gerät kein Anmeldebildschirm angezeigt werden muss.

Die Nutzung von 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 reibungslose Anmeldungen.
  • Komplexe Passwörter müssen nicht mehr für die Nutzer verwaltet werden.
  • Google überprüft die Identität des Nutzers.

Hinweis

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

Eigene Anwendung 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 Abhängigkeit der Google Play-Dienste für die Block Store API der Gradle-Build-Datei des Moduls hinzu. 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 es Entwicklern, Arrays mit einer Größe von bis zu 16 Byte zu speichern und wiederherzustellen. So können Sie wichtige Informationen zur aktuellen Nutzersitzung speichern und diese Informationen beliebig 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 zum Speichern eines Nutzertokens im Block Store behandelt. Die folgenden Schritte beschreiben, wie eine App, die Block Store nutzt, funktioniert:

  1. Während des Authentifizierungsvorgangs Ihrer App oder zu einem beliebigen späteren Zeitpunkt können Sie das Authentifizierungstoken des Nutzers zum späteren Abruf im Block Store speichern.
  2. Das Token wird lokal gespeichert und kann auch mit Ende-zu-Ende-Verschlüsselung in der Cloud gesichert werden.
  3. Die Daten werden übertragen, wenn der Nutzer auf einem neuen Gerät einen Wiederherstellungsvorgang startet.
  4. Wenn der Nutzer Ihre App während des Wiederherstellungsablaufs wiederherstellt, kann Ihre App das gespeicherte Token aus dem Block Store auf dem neuen Gerät abrufen.

Token speichern

Wenn sich ein Nutzer bei 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 von maximal 4 KB pro Eintrag speichern. Rufen Sie zum Speichern des Tokens setBytes() und setKey() für eine 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 das Token verschlüsselt und lokal auf dem Gerät gespeichert.

Im folgenden Beispiel wird gezeigt, 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 den Nutzer zuerst und rufen dann die Block Store-Daten ab. Der Nutzer hat bereits eingewilligt, Ihre App-Daten im Rahmen der Wiederherstellung wiederherzustellen, sodass keine zusätzlichen Einwilligungen erforderlich sind. Wenn der Nutzer Ihre App öffnet, können Sie Ihr Token vom Block Store anfordern, indem Sie retrieveBytes() aufrufen. Das abgerufene Token kann dann verwendet werden, damit der Nutzer auf dem neuen Gerät angemeldet bleibt.

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 für das Abrufen aller im BlockStore gespeicherten Tokens.

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 dafür, wie der Standardschlüssel abgerufen wird.

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 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 zum 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 die 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 überprüfen können, ob die Verschlüsselung während einer 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 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. Binde die BlockStore API in deine Test-App ein.
  2. Verwende die Test-App, um die BlockStore API zum Speichern deiner Daten aufzurufen.
  3. Deinstallieren Sie die Test-App und installieren Sie sie dann auf demselben Gerät neu.
  4. Verwenden Sie die Test-App, um die BlockStore API aufzurufen und Ihre Daten abzurufen.
  5. Überprü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 das Zielgerät dazu auf die Werkseinstellungen zurückgesetzt werden. Anschließend können Sie den Vorgang für die kabellose Android-Wiederherstellung oder die Google-Kabelwiederherstellung (bei unterstützten Geräten) starten.

Cloud-Wiederherstellung

  1. Binden Sie die Blockstore API in Ihre 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. Der Wert für shouldBackUpToCloud muss auf „true“ festgelegt sein.
  3. Auf Geräten mit O und höher können Sie manuell ein Block Store-Cloud-Back-up auslösen. Rufen Sie dazu 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 fünfminütige Wartezeit. Innerhalb dieser 5 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 einen Cloud-Wiederherstellungsvorgang 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 auf dem Zielgerät die Test-App, 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 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.

Ablauf der Geräte-zu-Gerät-Wiederherstellung

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

Auf den Quellgeräten muss für die Sicherung Android 6 (API 23) oder höher installiert sein.

Nehmen Sie eine Ausrichtung auf Geräte mit Android 9 (API-Level 29) und höher vor, 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 den Quellgeräten muss für die Sicherung Android 6 (API 23) oder höher installiert sein.

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