Blocca negozio

Molti utenti gestiscono ancora le proprie credenziali quando configurano un nuovo dispositivo con Android. Questo processo manuale può diventare difficile e spesso comporta un'esperienza utente scadente. L'API Block Store, una libreria basata su Google Play Services, mira a risolvere questo problema fornendo alle app un modo per salvare le credenziali utente senza la complessità o il rischio per la sicurezza associati al salvataggio delle password utente.

L'API Block Store consente alla tua app di archiviare i dati che può recuperare in un secondo momento per autenticare nuovamente gli utenti su un nuovo dispositivo. In questo modo, l'esperienza dell'utente è più fluida, in quanto non deve visualizzare una schermata di accesso quando avvia la tua app per la prima volta sul nuovo dispositivo.

I vantaggi dell'utilizzo di Block Store includono:

  • Soluzione di archivio credenziali criptato per gli sviluppatori. Le credenziali sono criptate end-to-end, se possibile.
  • Salva i token anziché i nomi utente e le password.
  • Elimina le difficoltà dei flussi di accesso.
  • Evita agli utenti l'onere di gestire password complesse.
  • Google verifica l'identità dell'utente.

Prima di iniziare

Per preparare l'app, completa i passaggi nelle sezioni seguenti.

Configura la tua app

Nel file build.gradle a livello di progetto, includi il repository Maven di Google nelle sezioni buildscript e allprojects:

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

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

Aggiungi la dipendenza Google Play Services per l'API Block Store al file di build Gradle del modulo, in genere app/build.gradle:

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

Come funziona

Block Store consente agli sviluppatori di salvare e ripristinare fino a 16 array di byte. In questo modo puoi salvare informazioni importanti sulla sessione utente corrente e hai la flessibilità di salvare queste informazioni come preferisci. Questi dati possono essere criptati end-to-end e l'infrastruttura che supporta Block Store è basata sull'infrastruttura di backup e ripristino.

Questa guida illustra il caso d'uso del salvataggio del token di un utente in Block Store. I seguenti passaggi descrivono il funzionamento di un'app che utilizza Block Store:

  1. Durante il flusso di autenticazione dell'app o in qualsiasi momento successivo, puoi archiviare il token di autenticazione dell'utente in Block Store per recuperarlo in un secondo momento.
  2. Il token verrà archiviato localmente e, se possibile, potrà essere sottoposto a backup nel cloud con crittografia end-to-end.
  3. I dati vengono trasferiti quando l'utente avvia un flusso di ripristino su un nuovo dispositivo.
  4. Se l'utente ripristina la tua app durante il flusso di ripristino, la tua app può recuperare il token salvato da Block Store sul nuovo dispositivo.

Salvataggio del token

Quando un utente accede alla tua app, puoi salvare il token di autenticazione che generi per quell'utente in Block Store. Puoi archiviare questo token utilizzando una coppia di valori chiave univoca con un massimo di 4 KB per voce. Per archiviare il token, chiama setBytes() e setKey() su un'istanza di StoreBytesData.Builder per archiviare le credenziali dell'utente sul dispositivo di origine. Dopo aver salvato il token con Block Store, il token viene criptato e archiviato localmente sul dispositivo.

L'esempio seguente mostra come salvare il token di autenticazione sul dispositivo locale:

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

Utilizza il token predefinito

I dati salvati utilizzando StoreBytes senza una chiave utilizzano la chiave predefinita 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)
    }

Recupero del token

In un secondo momento, quando un utente esegue il flusso di ripristino su un nuovo dispositivo, Google Play Services verifica prima l'utente, quindi recupera i dati di Block Store. L'utente ha già accettato di ripristinare i dati dell'app nell'ambito del flusso di ripristino, quindi non sono necessari ulteriori consensi. Quando l'utente apre la tua app, puoi richiedere il tuo token da Block Store chiamando retrieveBytes(). Il token recuperato può essere utilizzato per mantenere l'utente connesso sul nuovo dispositivo.

L'esempio seguente mostra come recuperare più token in base a chiavi specifiche.

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<String, BlockstoreData> blockstoreDataMap = result.getBlockstoreDataMap();
          for (Map.Entry<String, BlockstoreData> 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)
  }

Recupero di tutti i token.

Di seguito è riportato un esempio di come recuperare tutti i token salvati in BlockStore.

Java

BlockstoreClient client = Blockstore.getClient(this)

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

client.retrieveBytes(retrieveRequest)
    .addOnSuccessListener(
        result -> {
          Map<String, BlockstoreData> blockstoreDataMap = result.getBlockstoreDataMap();
          for (Map.Entry<String, BlockstoreData> 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)
  }

Di seguito è riportato un esempio di come recuperare la chiave predefinita.

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)

Eliminazione dei token

L'eliminazione dei token da BlockStore potrebbe essere necessaria per i seguenti motivi:

  • L'utente esegue il flusso di uscita.
  • Il token è stato revocato o non è valido.

Analogamente al recupero dei token, puoi specificare quali token devono essere eliminati impostando un array di chiavi che richiedono l'eliminazione.

L'esempio seguente mostra come eliminare determinate chiavi:

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)

Elimina tutti i token

L'esempio seguente mostra come eliminare tutti i token attualmente salvati in 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()
retrieve bytes, the key BlockstoreClient.DEFAULT_BYTES_DATA_KEY can be used
in the RetrieveBytesRequest instance in order to get your saved data

The following example shows how to retrieve the default key.

Java

End-to-end encryption

In order for end-to-end encryption to be made available, the device must be running Android 9 or higher, and the user must have set a screen lock (PIN, pattern, or password) for their device. You can verify if encryption will be available on the device by calling isEndToEndEncryptionAvailable().

The following sample shows how to verify if encryption will be available during cloud backup:

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

Attiva backup nel cloud

Per attivare il backup nel cloud, aggiungi il setShouldBackupToCloud() metodo all'oggetto StoreBytesData. Block Store eseguirà periodicamente il backup nel cloud dei byte archiviati quando setShouldBackupToCloud() è impostato su true.

L'esempio seguente mostra come attivare il backup nel cloud solo quando il backup nel cloud è criptato end-to-end:

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

Metodo di valutazione

Utilizza i seguenti metodi durante lo sviluppo per testare i flussi di ripristino.

Disinstallazione/reinstallazione sullo stesso dispositivo

Se l'utente attiva i servizi di backup (può essere verificato in Impostazioni > Google > Backup), i dati di Block Store vengono mantenuti durante la disinstallazione/reinstallazione dell'app.

Per eseguire il test, segui questi passaggi:

  1. Integra l'API Block Store nella tua app di test.
  2. Utilizza l'app di test per richiamare l'API Block Store per archiviare i dati.
  3. Disinstalla l'app di test e poi reinstallala sullo stesso dispositivo.
  4. Utilizza l'app di test per richiamare l'API Block Store per recuperare i dati.
  5. Verifica che i byte recuperati siano gli stessi archiviati prima della disinstallazione.

Da dispositivo a dispositivo

Nella maggior parte dei casi, sarà necessario ripristinare i dati di fabbrica del dispositivo di destinazione. Puoi quindi inserire il flusso di ripristino wireless di Android o il ripristino tramite cavo Google (per i dispositivi supportati).

Ripristino dal cloud

  1. Integra l'API Block Store nella tua app di test. L'app di test deve essere inviata al Play Store.
  2. Sul dispositivo di origine, utilizza l'app di test per richiamare l'API Block Store per archiviare i dati, con shouldBackUpToCloud impostato su true.
  3. Per i dispositivi con Android O e versioni successive, puoi attivare manualmente un backup nel cloud di Block Store: vai a Impostazioni > Google > Backup e fai clic sul pulsante "Esegui backup ora".
    1. Per verificare che il backup nel cloud di Block Store sia andato a buon fine, puoi:
      1. Al termine del backup, cerca le righe di log con il tag "CloudSyncBpTkSvc".
      2. Dovresti visualizzare righe simili a questa: "......, CloudSyncBpTkSvc: sync result: SUCCESS, ..., uploaded size: XXX bytes ..."
    2. Dopo un backup nel cloud di Block Store, è previsto un periodo di "raffreddamento" di 5 minuti. Entro questi 5 minuti, se fai clic sul pulsante "Esegui backup ora" non verrà attivato un altro backup nel cloud di Block Store.
  4. Ripristina i dati di fabbrica del dispositivo di destinazione ed esegui un flusso di ripristino dal cloud. Seleziona l'opzione per ripristinare l'app di test durante il flusso di ripristino. Per ulteriori informazioni sui flussi di ripristino dal cloud, consulta Flussi di ripristino dal cloud supportati.
  5. Sul dispositivo di destinazione, utilizza l'app di test per richiamare l'API Block Store per recuperare i dati.
  6. Verifica che i byte recuperati siano gli stessi archiviati nel dispositivo di origine.

Requisiti del dispositivo

Crittografia end-to-end

  • La crittografia end-to-end è supportata sui dispositivi con Android 9 (API 29) e versioni successive.
  • Per attivare la crittografia end-to-end e criptare correttamente i dati dell'utente, sul dispositivo deve essere impostato un blocco schermo con un PIN, una sequenza o una password.

Flusso di ripristino da dispositivo a dispositivo

Per il ripristino da dispositivo a dispositivo è necessario disporre di un dispositivo di origine e di un dispositivo di destinazione. Questi saranno i due dispositivi che trasferiranno i dati.

Per eseguire il backup, sui dispositivi di origine deve essere installato Android 6 (API 23) e versioni successive.

Per poter eseguire il ripristino, sui dispositivi di destinazione deve essere installato Android 9 (API 29) e versioni successive.

Ulteriori informazioni sul flusso di ripristino da dispositivo a dispositivo sono disponibili qui.

Flusso di backup e ripristino nel cloud

Per il backup e il ripristino nel cloud è necessario disporre di un dispositivo di origine e di un dispositivo di destinazione.

Per eseguire il backup, sui dispositivi di origine deve essere installato Android 6 (API 23) e versioni successive.

I dispositivi di destinazione sono supportati in base ai relativi fornitori. I dispositivi Pixel possono utilizzare questa funzionalità a partire da Android 9 (API 29), mentre su tutti gli altri dispositivi deve essere installato Android 12 (API 31) o versioni successive.