Bloquer le magasin

De nombreux utilisateurs gèrent toujours leurs propres identifiants lorsqu'ils configurent une nouvelle application Android appareil. Ce processus manuel peut s'avérer difficile et se traduire par une mauvaise l'expérience utilisateur. L'API Block Store, une bibliothèque fournie par Google Play des services Google, cherche à résoudre ce problème en permettant aux applications d'enregistrer sans la complexité ou les risques de sécurité associés à l'enregistrement mots de passe utilisateur.

L'API Block Store permet à votre application de stocker des données qu'elle pourra ensuite utiliser récupérer pour authentifier à nouveau les utilisateurs sur un nouvel appareil. Cela permet de fournir une une expérience fluide pour l'utilisateur, puisqu'il n'a pas besoin de voir un écran de connexion ; lorsque vous lancez votre application pour la première fois sur le nouvel appareil.

L'utilisation de Block Store offre les avantages suivants:

  • Solution de stockage d'identifiants chiffrés pour les développeurs Les identifiants sont chiffrés de bout en bout lorsque cela est possible.
  • Enregistrez des jetons au lieu des noms d'utilisateur et des mots de passe.
  • Éliminez les processus de connexion simplifiés.
  • Évitez aux utilisateurs d'avoir à gérer des mots de passe complexes.
  • Google valide l'identité de l'utilisateur.

Avant de commencer

Pour préparer votre application, procédez comme indiqué dans les sections suivantes.

Configurer votre application

Dans le fichier build.gradle au niveau du projet, incluez l'API Maven de Google d'intégration dans votre buildscript et allprojects:

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

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

Ajoutez les services Google Play. pour l'API Block Store fichier de compilation Gradle du module, qui est généralement app/build.gradle:

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

Fonctionnement

Block Store permet aux développeurs d'enregistrer et de restaurer des tableaux de 16 octets maximum. Cela vous permet d'enregistrer des informations importantes concernant la session utilisateur en cours et de les enregistrer comme vous le souhaitez. Ces données peuvent être chiffrées de bout en bout, et l'infrastructure sur laquelle repose le Block Store est basée sur l'infrastructure de sauvegarde et de restauration.

Ce guide décrit le cas d'utilisation de l'enregistrement du jeton d'un utilisateur dans Block Store. Les étapes suivantes décrivent le fonctionnement d'une application utilisant Block Store:

  1. Pendant le flux d'authentification de votre application ou à tout moment par la suite, vous pouvez stocker le jeton d'authentification de l'utilisateur à Block Store pour le récupérer ultérieurement.
  2. Le jeton sera stocké localement et peut également être sauvegardé dans le cloud, chiffrés de bout en bout lorsque cela est possible.
  3. Les données sont transférées lorsque l'utilisateur lance un flux de restauration sur un nouvel appareil.
  4. Si l'utilisateur restaure votre application pendant le flux de restauration, votre application peut récupérer le jeton enregistré à partir du Block Store sur le nouvel appareil.

Enregistrer le jeton

Lorsqu'un utilisateur se connecte à votre application, vous pouvez enregistrer dans le Block Store le jeton d'authentification que vous avez généré pour cet utilisateur. Vous pouvez stocker ce jeton à l'aide d'une valeur de paire de clés unique avec un maximum de 4 Ko par entrée. Pour stocker le jeton, appelez setBytes(). et setKey() sur une instance de StoreBytesData.Builder pour stocker les identifiants de l'utilisateur sur l'appareil source. Après avoir enregistré le jeton avec Block Store, le jeton est chiffré et stocké localement sur l'appareil.

L'exemple suivant montre comment enregistrer le jeton d'authentification dans l'appareil local:

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

Utiliser le jeton par défaut

Les données enregistrées à l'aide de StoreBytes sans clé utilisent la clé par défaut 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)
    }

Récupérer le jeton

Plus tard, lorsqu'un utilisateur suit le flux de restauration sur une nouvelle appareil, les services Google Play vérifient d'abord l'utilisateur, puis récupère votre Stocker des données L'utilisateur a déjà accepté de restaurer les données de votre application dans le cadre de le flux de restauration. Par conséquent, aucun consentement supplémentaire n'est requis. Lorsque l'utilisateur ouvre votre application, vous pouvez demander votre jeton à Block Store en appelant retrieveBytes() Le jeton récupéré peut ensuite être utilisé pour garder l'utilisateur connecté à la nouvelle appareil.

L'exemple suivant montre comment récupérer plusieurs jetons en fonction de clés spécifiques.

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

Récupération de tous les jetons...

Vous trouverez ci-dessous un exemple de récupération de tous les jetons enregistrés dans BlockStore.

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

Vous trouverez ci-dessous un exemple de récupération de la clé par défaut.

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)

Supprimer des jetons

Il peut être nécessaire de supprimer des jetons de BlockStore pour les raisons suivantes:

  • L'utilisateur suit le flux de déconnexion.
  • Le jeton a été révoqué ou n'est pas valide.

Comme pour la récupération de jetons, vous pouvez spécifier les jetons à supprimer en définissant un tableau de clés à supprimer.

Vous trouverez ci-dessous un exemple de suppression de certaines clés.

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)

Supprimer tous les jetons

L'exemple ci-dessous supprime tous les jetons actuellement enregistrés dans BlockStore:

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

Chiffrement de bout en bout

Pour que le chiffrement de bout en bout soit disponible, l'appareil doit être fonctionnant sous Android 9 ou version ultérieure, et l'utilisateur doit avoir configuré le verrouillage de l'écran (code, schéma ou mot de passe) de son appareil. Vous pouvez vérifier si le chiffrement être disponible sur l'appareil en appelant isEndToEndEncryptionAvailable().

L'exemple suivant montre comment vérifier si le chiffrement est disponible pendant sauvegarde dans le cloud:

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

Activer la sauvegarde dans le cloud

Pour activer la sauvegarde dans le cloud, ajoutez le setShouldBackupToCloud() à votre StoreBytesData . Block Store sauvegarde périodiquement dans le cloud les octets stockés lorsque setShouldBackupToCloud() est défini sur "true".

L'exemple suivant montre comment activer la sauvegarde dans le cloud uniquement lorsque la sauvegarde dans le cloud est activée est chiffré de bout en bout :

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

Procédure de test

Utilisez les méthodes suivantes pendant le développement afin de tester la restauration les flux de données.

Désinstallation/réinstallation sur le même appareil

Si l'utilisateur active les services de sauvegarde (vous pouvez le vérifier dans Paramètres > Google > Sauvegarde). Les données du Block Store sont conservées lors de la désinstallation/réinstallation de l'application.

Pour ce faire, procédez comme suit:

  1. Intégrez l'API BlockStore à votre application de test.
  2. Utilisez l'application de test pour appeler l'API BlockStore afin de stocker vos données.
  3. Désinstallez votre application de test, puis réinstallez-la sur le même appareil.
  4. Utilisez l'application de test pour appeler l'API BlockStore afin de récupérer vos données.
  5. Vérifier que les octets récupérés sont identiques à ceux stockés avant désinstallation.

Appareil à appareil

Dans la plupart des cas, cela nécessite de rétablir la configuration d'usine de l'appareil cible. Vous pouvez puis accédez au flux de restauration sans fil Android. ou restauration d'un câble Google (pour les appareils compatibles).

Restauration dans le cloud

  1. Intégrez l'API Blockstore à votre application de test. L'application de test doit être envoyées au Play Store.
  2. Sur l'appareil source, utilisez l'application de test pour appeler l'API Blockstore et stocker vos données, avec shouldBackUpToCloud défini sur true.
  3. Pour les appareils O et supérieurs, vous pouvez déclencher manuellement une sauvegarde dans le cloud Block Store: accédez à Paramètres > Google > Sauvegarder, cliquez sur le bouton "Sauvegarder maintenant".
    1. Pour vérifier que la sauvegarde dans le cloud Block Store a réussi, vous pouvez:
      1. Une fois la sauvegarde terminée, recherchez les lignes de journal avec balise "CloudSyncBpTkSvc".
      2. Les lignes suivantes doivent s'afficher : "......, CloudSyncBpTkSvc: sync" résultat: SUCCÈS, ..., taille d'importation: XXX octets ..."
    2. Après une sauvegarde dans le cloud du Block Store, vous disposez d'un délai d'attente de cinq minutes. Au cours de ces cinq minutes, le fait de cliquer sur le bouton "Sauvegarder maintenant" une autre sauvegarde dans le cloud Block Store.
  4. Rétablissez la configuration d'usine de l'appareil cible et suivez un processus de restauration dans le cloud. Sélectionner pour restaurer votre application de test pendant le flux de restauration. Pour en savoir plus sur flux de restauration cloud, consultez la section Flux de restauration cloud compatibles.
  5. Sur l'appareil cible, utilisez l'application de test pour appeler l'API Blockstore. pour récupérer vos données.
  6. Vérifiez que les octets récupérés sont identiques à ceux stockés dans le appareil source.

Configuration requise pour l'appareil

Chiffrement de bout en bout

  • Le chiffrement de bout en bout est compatible avec les appareils équipés d'Android 9 (API 29) ou version ultérieure.
  • Pour activer le chiffrement de bout en bout et chiffrer correctement les données de l'utilisateur, le verrouillage de l'écran doit être défini sur l'appareil avec un code, un schéma ou un mot de passe.

Processus de restauration d'appareil à appareil

La restauration d'appareil à appareil nécessite un appareil source et un appareil cible. Ce seront les deux appareils qui transféreront les données.

Les appareils sources doivent exécuter Android 6 (API 23) ou une version ultérieure pour pouvoir effectuer une sauvegarde.

Ciblez les appareils équipés d'Android 9 (API 29) ou version ultérieure pour qu'ils puissent effectuer la restauration.

Pour en savoir plus sur le processus de restauration d'appareil à appareil, cliquez ici.

Flux de sauvegarde et de restauration dans le cloud

La sauvegarde dans le cloud et la restauration nécessitent un appareil source et un appareil cible.

Les appareils sources doivent exécuter Android 6 (API 23) ou une version ultérieure pour pouvoir effectuer une sauvegarde.

Les appareils cibles sont acceptés en fonction de leurs fournisseurs. Les appareils Pixel peuvent utiliser cette fonctionnalité à partir d'Android 9 (API 29). Tous les autres appareils doivent être équipés d'Android 12 (API 31) ou version ultérieure.