ブロックストア

多くのユーザーは、新しい Android デバイスを設定するときに自身の認証情報を管理します。この手動プロセスは困難になりがちであり、多くの場合、ユーザー エクスペリエンスが低下します。Google Play 開発者サービスを利用したライブラリである Block Store API は、アプリでユーザー認証情報の保存に伴う複雑さやセキュリティ リスクを回避してユーザーの認証情報を保存する方法を提供することで、この問題を解決しようとしています。

Block Store API を使用すると、アプリはユーザー認証情報を保存して、後で新しいデバイスでユーザーの再認証用に取得できます。これにより、ユーザーは新しいデバイスで初めてアプリを起動する際にログイン画面を表示する必要がないため、シームレスなエクスペリエンスを実現できます。

Block Store を使用するメリットは次のとおりです。

  • デベロッパー向けの暗号化された認証情報ストレージ ソリューション。認証情報は、可能な場合はエンドツーエンドで暗号化されます。
  • ユーザー名とパスワードの代わりにトークンを保存します。
  • ログインフローによる摩擦を排除します。
  • 複雑なパスワードを管理する負担からユーザーを解放。
  • Google がユーザーの本人確認を行います。

始める前に

アプリを準備するには、以下のセクションに示す手順を完了します。

アプリを設定する

プロジェクト レベルの build.gradle ファイルの buildscript セクションと allprojects セクションの両方に、Google' の Maven リポジトリをインクルードします。

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

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

Module' Gradle ビルドファイル(通常は app/build.gradle)に Block Store API への Google Play 開発者サービスの依存関係を追加します。

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

仕組み

ブロックストアは、エンドツーエンドの暗号化を採用し、バックアップと復元のインフラストラクチャ上に構築される、トークンベースのログイン メカニズムです。Block Store を利用するアプリの動作の概要を次に示します。

  1. アプリの認証フロー中またはその後の任意の時点で、後で取得するために、ユーザーの認証トークンをブロックストアに保存できます。
  2. トークンはローカルに保存されます。また、クラウド内でのバックアップも可能で、可能な場合はエンドツーエンドの暗号化も行います。
  3. ユーザーが新しいデバイスで復元フローを開始すると、データが転送されます。
  4. ユーザーが復元フロー中にアプリを復元すると、アプリは新しいデバイスのブロックストアから保存済みのトークンを取得できます。

トークンの保存

ユーザーがアプリにログインしたら、そのユーザーのために生成した認証トークンを Block Store に保存できます。これを行うには、StoreBytesData.Builder のインスタンスで setBytes() を呼び出して、ユーザーの認証情報をソースデバイスに保存します。Block Store を使用してトークンを保存すると、トークンが暗号化され、デバイス上でローカルに保存されます。

次のサンプルは、認証トークンをローカル デバイスに保存する方法を示しています。

val client = Blockstore.getClient(this)
val data = StoreBytesData.Builder()
        .setBytes(/* BYTE_ARRAY */)
        .build()
client.storeBytes(data)
        .addOnSuccessListener{ result ->
            Log.d(TAG, "Stored: ${result} bytes")
        }
        .addOnFailureListener { e ->
            Log.e(TAG, “Failed to store bytes”, e)
        }

トークンの取得

その後、ユーザーが新しいデバイスで復元フローを実行すると、Google Play 開発者サービスはまずユーザーを検証してから、ブロックストア データを取得します。ユーザーは復元フローの一環としてアプリデータの復元にすでに同意しているため、追加同意は必要ありません。ユーザーがアプリを開いたら、retrieveBytes() を呼び出して、Block Store からトークンをリクエストできます。取得したトークンを使用して、新しいデバイスでユーザーのログイン状態を維持できます。

次のサンプルは、以前にブロックストアに保存されている暗号化されたトークンを取得する方法を示しています。

val client = Blockstore.getClient(this)
client.retrieveBytes()
            .addOnSuccessListener { result ->
                Log.d(TAG, "Retrieved: ${String(result)}")
            }
            .addOnFailureListener { e ->
                Log.e(TAG, "Failed to retrieve bytes", e)
            }
}

エンドツーエンドの暗号化

エンドツーエンドの暗号化を利用できるようにするには、デバイスに Android 9 以降が搭載されている必要があります。また、ユーザーはデバイスの画面ロック(PIN、パターン、またはパスワード)を設定する必要があります。デバイスで暗号化を使用できるかどうかを確認するには、isEndToEndEncryptionAvailable() を呼び出します。

次のサンプルは、クラウドのバックアップ中に暗号化が利用可能かどうかを確認する方法を示しています。

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

クラウド バックアップを有効にする

クラウド バックアップを有効にするには、StoreBytesData オブジェクトに setShouldBackupToCloud() メソッドを追加します。Block Store は、setShouldBackupToCloud() が true に設定されている場合、保存されたバイトを定期的にクラウドにバックアップします。

次のサンプルは、クラウドのバックアップがエンドツーエンドで暗号化されている場合にのみクラウドのバックアップを有効にする方法を示しています。

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

テスト方法

復元フローをテストするには、開発中に以下の方法を使用します。

同じデバイスのアンインストール/再インストール

ユーザーがバックアップ サービスを有効にしている場合([設定] > [Google] > [バックアップ] で確認できます)、ブロックストアのデータはアンインストール中や再インストール後も保持されます。

以下の手順でテストできます。

  1. BlockStore API をテストアプリに統合する。
  2. テストアプリを使用して、BlockStore API を呼び出してデータを保存します。
  3. テストアプリをアンインストールし、同じデバイスにアプリを再インストールする。
  4. テストアプリを使用して BlockStore API を呼び出し、データを取得する。
  5. 取得したバイト数が、アンインストール前に保存されたバイト数と同じであることを確認します。

デバイス間

ほとんどの場合、対象デバイスを出荷時の設定にリセットする必要があります。その後、Android のワイヤレス 復元フローまたは Google ケーブル復元(サポートされているデバイスの場合)を入力できます。

クラウドの復元

  1. Blockstore API をテストアプリに統合します。テストアプリを Play ストアに送信する必要があります。
  2. ソースデバイスで、テストアプリを使用して Blockstore API を呼び出し、shouldBackUpToCloud を true に設定してデータを保存します。
  3. O 以降のデバイスでは、Block Store クラウド バックアップを手動でトリガーできます。[設定] > [Google] > [バックアップ] に移動し、[今すぐバックアップ] ボタンをクリックしてください。
    1. Block Store のクラウド バックアップが成功したことを確認するには、次の方法があります。
      1. バックアップが完了したら、「CloudSyncBpTkSvc」タグが付いたログ行を検索します。
      2. 次のような行が表示されます。「......、CloudSyncBpTkSvc: sync result: SUCCESS, ..., upload size: XXX bytes ...」
    2. Block Store のクラウド バックアップの後、5 分間の「クールダウン」期間があります。その 5 分以内に [今すぐバックアップ] ボタンをクリックしても、Block Store の別のクラウド バックアップがトリガーされることはありません。
  4. 対象デバイスを出荷時の設定にリセットし、クラウドの復元フローに沿って操作する。復元フロー中にテストアプリを復元する場合に選択します。クラウドの復元フローの詳細については、サポートされているクラウドの復元フローをご覧ください。
  5. ターゲット デバイスで、テストアプリを使用して Blockstore API を呼び出し、データを取得します。
  6. 取得したバイトが、ソースデバイスに保存されているものと同じであることを確認します。

デバイスの要件

エンドツーエンドの暗号化

  • Android 9(API 29)以降を搭載するデバイスでは、エンドツーエンドの暗号化がサポートされています。
  • デバイスでエンドツーエンドの暗号化を有効にし、ユーザーのデータを正しく暗号化するには、デバイスで画面ロック、PIN、パターン、またはパスワードを設定する必要があります。

デバイス間の復元フロー

デバイス間の復元には、ソースデバイスとターゲット デバイスが必要です。この 2 つのデバイスがデータを転送します。

ソースデバイスのバックアップには、Android 6(API 23)以降が搭載されている必要があります。

Android 9(API 29)以降を実行しているデバイスをターゲットにし、復元できるようにします。

デバイス間の復元フローについて詳しくは、こちらをご確認ください。

クラウドのバックアップと復元のフロー

クラウドのバックアップと復元には、ソースデバイスとターゲット デバイスが必要です。

ソースデバイスのバックアップには、Android 6(API 23)以降が搭載されている必要があります。

対象デバイスは、そのベンダーに基づいてサポートされています。Pixel デバイスは Android 9(API 29)からこの機能を使用できます。その他のすべてのデバイスは Android 12(API 31)以降を搭載している必要があります。