Molti utenti gestiscono ancora le proprie credenziali durante la configurazione di un nuovo dispositivo Android. Questo processo manuale può diventare difficile e spesso offre un'esperienza utente scadente. L'API Block Store, una libreria basata su Google Play Services, cerca di 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 in un secondo momento può recuperare per autenticare nuovamente gli utenti su un nuovo dispositivo. Questo contribuisce a offrire un'esperienza più fluida all'utente, che non deve vedere una schermata di accesso al primo avvio dell'app sul nuovo dispositivo.
I vantaggi dell'utilizzo del Block Store includono:
- Soluzione di archiviazione di credenziali criptata per gli sviluppatori. Quando possibile, le credenziali vengono criptate end-to-end.
- Salva i token anziché i nomi utente e le password.
- Elimina l'attrito dai flussi di accesso.
- Evita agli utenti la necessità di gestire password complesse.
- Google verifica l'identità dell'utente.
Prima di iniziare
Per preparare la tua 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 tuo file di build Gradle del modulo, che in genere è app/build.gradle
:
dependencies {
implementation 'com.google.android.gms:play-services-auth-blockstore:16.2.0'
}
Come funziona
Block Store consente agli sviluppatori di salvare e ripristinare fino a array da 16 byte. In questo modo è possibile salvare informazioni importanti relative alla sessione utente corrente e offre la flessibilità di salvarle come preferisce. Questi dati possono essere protetti con crittografia end-to-end e l'infrastruttura che supporta Block Store è basata sull'infrastruttura Backup e ripristino.
Questa guida illustra il caso d'uso del salvataggio del token di un utente nel Block Store. I seguenti passaggi descrivono il funzionamento di un'app che utilizza Block Store:
- Durante il flusso di autenticazione della tua app o in un secondo momento, puoi memorizzare il token di autenticazione dell'utente nel Block Store per recuperarlo in un secondo momento.
- Il token verrà archiviato in locale e, se possibile, verrà eseguito anche il backup del cloud, che sarà protetto con crittografia end-to-end.
- I dati vengono trasferiti quando l'utente avvia un flusso di ripristino su un nuovo dispositivo.
- Se l'utente ripristina l'app durante il flusso di ripristino, l'app può quindi recuperare il token salvato dal 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 al Block Store. Puoi memorizzare questo token utilizzando una coppia di chiavi univoca che abbia un massimo di 4 kb per voce.
Per archiviare il token, chiama setBytes()
e 'setKey(/android/reference/com/google/android/gms/auth/blockstore/StoreBytesData.Builder.html#setKey(java.lang.String)' su un'istanza di
StoreBytesData.Builder
per archiviare le credenziali dell'utente sul dispositivo di origine. Dopo averlo salvato con il Block Store, il token viene criptato e archiviato localmente sul dispositivo.
Il seguente esempio 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 token predefinito
I dati salvati utilizzando StoreBytes senza una chiave utilizzano la chiave predefinita StorestoreClient.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 corso...
Successivamente, quando un utente esegue il flusso di ripristino su un nuovo
dispositivo, Google Play Services verifica prima l'utente, quindi recupera i dati del Block Store. L'utente ha già accettato di ripristinare i dati della tua app come parte del flusso di ripristino, quindi non sono necessari altri consensi. Quando l'utente apre l'app, puoi richiedere il token nel Block Store chiamando il numero retrieveBytes()
.
Il token recuperato può quindi essere utilizzato per mantenere l'accesso dell'utente 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 ListrequestedKeys = 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) }
Recupero di tutti i token in corso...
Di seguito è riportato un esempio di come recuperare tutti i token salvati nel BlockStore.
Java
BlockstoreClient client = Blockstore.getClient(this) // Retrieve all data. RetrieveBytesRequest retrieveRequest = new RetrieveBytesRequest.Builder() .setRetrieveAll(true) .build(); client.retrieveBytes(retrieveRequest) .addOnSuccessListener( result -> { MapblockstoreDataMap = 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) }
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 dal BlockStore può essere richiesta per i seguenti motivi:
- L'utente si disconnette.
- 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.
Di seguito è riportato un esempio per l'eliminazione di 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 ListrequestedKeys = 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 elimina tutti i token attualmente salvati nel 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") }
Crittografia end-to-end
Affinché la crittografia end-to-end sia disponibile, il dispositivo deve eseguire Android 9 o versioni successive e l'utente deve aver impostato un blocco schermo (PIN, sequenza o password) per il proprio dispositivo. Puoi verificare se la crittografia sarà disponibile sul dispositivo chiamando isEndToEndEncryptionAvailable()
.
L'esempio seguente mostra come verificare se la crittografia sarà disponibile durante il backup sul cloud:
client.isEndToEndEncryptionAvailable()
.addOnSuccessListener { result ->
Log.d(TAG, "Will Block Store cloud backup be end-to-end encrypted? $result")
}
Abilita il backup sul cloud
Per abilitare il backup del cloud, aggiungi il metodo setShouldBackupToCloud()
al tuo oggetto StoreBytesData
. Il Block Store esegue periodicamente il backup sul cloud dei byte archiviati quando
setShouldBackupToCloud()
è impostato su true.
L'esempio seguente mostra come abilitare il backup sul cloud solo quando il backup sul cloud è protetto con crittografia 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.")
}
}
Come eseguire il test
Utilizza i seguenti metodi durante lo sviluppo per testare i flussi di ripristino.
Disinstalla/reinstalla lo stesso dispositivo
Se l'utente attiva i servizi di backup (puoi controllarlo in Impostazioni > Google > Backup), i dati del Block Store vengono conservati durante la disinstallazione/reinstallazione dell'app.
Per eseguire il test, segui questi passaggi:
- Integra l'API BlockStore nella tua app di prova.
- Utilizza l'app di prova per richiamare l'API BlockStore per archiviare i tuoi dati.
- Disinstalla l'app di prova e reinstalla l'app sullo stesso dispositivo.
- Utilizza l'app di prova per richiamare l'API BlockStore per recuperare i tuoi dati.
- Verifica che i byte recuperati corrispondano a quelli memorizzati prima della disinstallazione.
Dispositivo a dispositivo
Nella maggior parte dei casi, sarà necessario ripristinare i dati di fabbrica del dispositivo di destinazione. Dopodiché puoi accedere al flusso di ripristino wireless di Android o al ripristino dei cavi di Google (per i dispositivi supportati).
Ripristino dal cloud
- Integra l'API Blockstore nella tua app di test. L'app di test deve essere inviata al Play Store.
- Sul dispositivo di origine, utilizza l'app di prova per richiamare l'API Blockstore per archiviare i dati, con ShouldBackUpToCloud impostato su true.
- Per i dispositivi O e versioni successive, puoi attivare manualmente un backup sul cloud di Block Store:
vai a Impostazioni > Google > Backup e fai clic sul pulsante "Esegui il backup ora".
- Per verificare che il backup sul cloud del Block Store sia riuscito, puoi:
- Al termine del backup, cerca le righe di log con il tag "CloudSyncBpTkSvc".
- Dovresti vedere righe come la seguente: "......, CloudSyncBpTkSvc: sync result: SUCCESS, ..., dimensioni caricate: XXX byte ..."
- Dopo il backup sul cloud del Block Store, è previsto un periodo di "raffreddamento" di 5 minuti. Entro 5 minuti, facendo clic sul pulsante "Effettua ora il backup", non verrà attivato un altro backup del cloud nel Block Store.
- Per verificare che il backup sul cloud del Block Store sia riuscito, puoi:
- Ripristina i dati di fabbrica del dispositivo di destinazione e segui il flusso di ripristino del cloud. Seleziona per ripristinare l'app di prova durante il flusso di ripristino. Per ulteriori informazioni sui flussi di ripristino del cloud, consulta Flussi di ripristino del cloud supportati.
- Sul dispositivo di destinazione, utilizza l'app di prova per richiamare l'API Blockstore per recuperare i tuoi dati.
- Verifica che i byte recuperati corrispondano a quelli 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.
- Il dispositivo deve avere un blocco schermo impostato con un PIN, una sequenza o una password per attivare la crittografia end-to-end e criptare correttamente i dati dell'utente.
Flusso di ripristino tra dispositivi
Il ripristino da dispositivo a dispositivo richiederà un dispositivo di origine e un dispositivo di destinazione. Questi saranno i due dispositivi che trasferiscono i dati.
Per il backup, i dispositivi di origine devono eseguire Android 6 (API 23) e versioni successive.
Scegli come target dispositivi con Android 9 (API 29) e versioni successive per poter ripristinare il dispositivo.
Ulteriori informazioni sul flusso di ripristino dei dispositivi sono disponibili qui.
Flusso Cloud Backup e ripristino
Il backup e il ripristino del cloud richiedono un dispositivo di origine e un dispositivo di destinazione.
Per il backup, i dispositivi di origine devono eseguire Android 6 (API 23) e versioni successive.
I dispositivi target sono supportati in base ai relativi fornitori. I dispositivi Pixel possono utilizzare questa funzionalità a partire da Android 9 (API 29), mentre tutti gli altri dispositivi devono utilizzare Android 12 (API 31) o versioni successive.