Loja de blocos

Muitos usuários ainda gerenciam as próprias credenciais ao configurar uma nova versão do Android dispositivo. Esse processo manual pode se tornar desafiador e, muitas vezes, resulta em uma experiência do usuário. A API Block Store, uma biblioteca com tecnologia do Google Play serviços, busca resolver isso oferecendo uma maneira de os aplicativos salvarem credenciais do usuário sem os riscos de segurança e complexidade associados senhas de usuário.

A API Block Store permite que seu aplicativo armazene dados que podem ser usados mais tarde recuperar para reautenticar os usuários em um novo dispositivo. Isso ajuda a fornecer perfeita para o usuário, já que ele não precisa ver uma tela de login ao iniciar o app pela primeira vez em um novo dispositivo.

Os benefícios de usar a Block Store incluem:

  • Solução de armazenamento de credenciais criptografado para desenvolvedores. As credenciais são com criptografia de ponta a ponta sempre que possível.
  • Salvar tokens em vez de nomes de usuário e senhas.
  • Elimine o atrito dos fluxos de login.
  • Evite que os usuários precisem gerenciar senhas complexas.
  • O Google verifica a identidade do usuário.
.

Antes de começar

Para preparar o app, siga as etapas nas seções a seguir.

Configurar o app

No arquivo build.gradle no nível do projeto, inclua o Maven do Google repositório em buildscript e allprojects seções:

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

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

Adicionar o Google Play Services da API Block Store ao seu arquivo de build do Gradle do módulo, que é comumente app/build.gradle:

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

Como funciona

O armazenamento de blocos permite que os desenvolvedores salvem e restaurem matrizes de até 16 bytes. Isso permite salvar informações importantes relacionadas à sessão atual do usuário e oferece a flexibilidade de salvar essas informações como você quiser. Esses dados podem ser criptografados de ponta a ponta, e a infraestrutura que oferece suporte ao armazenamento em blocos é construída sobre a infraestrutura de backup e restauração.

Este guia aborda o caso de uso de salvar o token de um usuário no Block Store. As etapas a seguir descrevem como um app que utiliza a Block Store funcionaria:

  1. Durante o fluxo de autenticação do app ou a qualquer momento depois disso, é possível armazenar o token de autenticação do usuário ao armazenamento bloqueado para recuperação posterior.
  2. O token será armazenado localmente e também poderá ser salvo em backup na nuvem. com criptografia de ponta a ponta sempre que possível.
  3. Os dados são transferidos quando o usuário inicia um fluxo de restauração em um novo dispositivo.
  4. Se o usuário restaurar o app durante o fluxo, ele poderá recuperar o token salvo da Block Store no novo dispositivo.

Como salvar o token

Quando um usuário faz login no app, é possível salvar o token de autenticação gerado para ele na loja bloqueada. É possível armazenar esse token usando um valor de par de chaves exclusivo com no máximo 4 KB por entrada. Para armazenar o token, chame setBytes(). e setKey() em uma instância do StoreBytesData.Builder para armazenar as credenciais do usuário no dispositivo de origem. Depois de salvar o token com o armazenamento em bloco, o token é criptografado e armazenado localmente no dispositivo.

O exemplo a seguir mostra como salvar o token de autenticação no dispositivo 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)
    }

Usar token padrão

Os dados salvos usando StoreBytes sem uma chave usam a chave padrão 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)
    }

Como recuperar o token

Depois, quando um usuário passar pelo fluxo de restauração em um novo dispositivo, o Google Play Services primeiro verifica o usuário e, em seguida, recupera seu bloco Armazenar dados. O usuário já concordou em restaurar os dados do app como parte do no fluxo de restauração. Nenhum consentimento adicional é necessário. Quando o usuário abre seu app, é possível solicitar seu token da Block Store chamando retrieveBytes() O token recuperado pode ser usado para manter o usuário conectado ao novo dispositivo.

O exemplo a seguir mostra como recuperar vários tokens com base em chaves específicas.

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

Recuperando todos os tokens.

Abaixo está um exemplo de como recuperar todos os tokens salvos no 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)
  }

Confira abaixo um exemplo de como recuperar a chave padrão.

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)

Como excluir tokens

A exclusão de tokens do BlockStore pode ser necessária pelos seguintes motivos:

  • O usuário passa pelo fluxo de usuário para sair.
  • O token foi revogado ou é inválido.

Assim como na recuperação de tokens, é possível especificar quais tokens precisam ser excluídos definindo uma matriz de chaves que exigem exclusão.

Confira abaixo um exemplo de como excluir determinadas chaves.

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)

Excluir todos os tokens

O exemplo abaixo exclui todos os tokens salvos atualmente no 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")
  }

Criptografia de ponta a ponta

Para que a criptografia de ponta a ponta seja disponibilizada, o dispositivo deve ser com o Android 9 ou versões mais recentes, e o usuário precisa ter definido um bloqueio de tela (PIN, padrão ou senha) do dispositivo. É possível verificar se a criptografia disponíveis no dispositivo chamando isEndToEndEncryptionAvailable().

O exemplo a seguir mostra como verificar se a criptografia estará disponível durante backup na nuvem:

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

Ativar backup na nuvem

Para ativar o backup na nuvem, adicione o setShouldBackupToCloud() ao seu método StoreBytesData objeto. O Block Store faz backup periódico dos bytes armazenados na nuvem setShouldBackupToCloud() foi definido como verdadeiro.

O exemplo a seguir mostra como ativar o backup na nuvem somente quando o backup são criptografados de ponta a ponta:

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

Como testar

Use os seguintes métodos durante o desenvolvimento para testar a restauração flui.

Desinstalar/reinstalar no mesmo dispositivo

Se o usuário ativar os serviços de backup (essa opção pode ser verificada em Configurações > Google > Backup), é possível bloquear dados da loja persistiu durante a desinstalação/reinstalação do app.

Siga estas etapas para testar:

  1. Integre a API BlockStore ao app de teste.
  2. Use o app de teste para invocar a API BlockStore e armazenar seus dados.
  3. Desinstale o app de teste e reinstale-o no mesmo dispositivo.
  4. Use o app de teste para invocar a API BlockStore e recuperar seus dados.
  5. Verificar se os bytes recuperados são os mesmos armazenados anteriormente desinstalação.
.

Entre dispositivos

Na maioria dos casos, isso exige a redefinição do dispositivo de destino para a configuração original. Você pode Em seguida, entre no fluxo de restauração sem fio do Android. ou restauração do cabo do Google (para dispositivos compatíveis).

Restauração na nuvem

  1. Integrar a API Blockstore ao app de teste. O app de teste precisa ser enviados à Play Store.
  2. No dispositivo de origem, use o app de teste para invocar a API Blockstore e armazenar seus dados, com shouldBackUpToCloud definido como verdadeiro.
  3. Para dispositivos O e mais recentes, é possível acionar manualmente um backup na nuvem do Block Store: vá para Configurações > Google > Backup, clique no botão "Fazer backup agora".
    1. Para verificar se o backup na nuvem do Block Store foi concluído, é possível:
      1. Depois que o backup for concluído, pesquise as linhas de registro com a tag "CloudSyncBpTkSvc".
      2. Você verá linhas como estas: “......, CloudSyncBpTkSvc: sync resultado: SUCCESS, ..., tamanho do upload: XXX bytes ...”
    2. Depois de um backup na nuvem da Block Store, há um período de "resfriamento" de cinco minutos. Em até cinco minutos, clicar no botão "Fazer backup agora" não será acionado outro backup na nuvem da Block Store.
  4. redefinir o dispositivo de destino para a configuração original e passar por um fluxo de restauração na nuvem; Selecionar para restaurar o app de teste durante o fluxo de restauração. Para mais informações sobre para saber mais sobre os fluxos de restauração na nuvem, consulte Fluxos de restauração na nuvem com suporte.
  5. No dispositivo de destino, use o app de teste para invocar a API Blockstore e recuperar seus dados.
  6. Verifique se os bytes recuperados são os mesmos armazenados no dispositivo de origem.

Requisitos do dispositivo

Criptografia de ponta a ponta

  • A criptografia de ponta a ponta está disponível em dispositivos com o Android 9 (API 29) e versões mais recentes.
  • O dispositivo precisa ter um bloqueio de tela definido com um PIN, um padrão ou uma senha para que a criptografia de ponta a ponta seja ativada e criptografe corretamente os dados do usuário.
.

Fluxo de restauração de dispositivo para dispositivo

A restauração entre dispositivos exige que você tenha um dispositivo de origem e outro de destino. Esses serão os dois dispositivos que estão transferindo dados.

Para fazer backup, os dispositivos de origem precisam ter o Android 6 (API 23) ou uma versão mais recente.

Destinar a dispositivos com o Android 9 (API 29) e versões mais recentes que possam ser restaurados.

Confira mais informações sobre o fluxo de restauração de dispositivo para dispositivo neste link.

Fluxo de backup e restauração na nuvem

O backup e a restauração na nuvem vão exigir um dispositivo de origem e um de destino.

Para fazer backup, os dispositivos de origem precisam ter o Android 6 (API 23) ou uma versão mais recente.

Os dispositivos de destino são compatíveis com base nos fornecedores. Os dispositivos Pixel podem usar esse recurso do Android 9 (nível 29 da API) e todos os outros precisam ter o Android 12 (nível 31 da API) ou uma versão mais recente.