認証情報マネージャーを「Google でログイン」と統合する

Google でログインを使用すると、ユーザー認証を Android アプリにすばやく統合できます。ユーザーは、Google アカウントを使用してアプリにログインし、同意を行い、プロフィール情報をアプリと安全に共有できます。Android の認証情報マネージャー Jetpack ライブラリを使用すると、この統合をスムーズに行うことができ、1 つの API を使用して Android デバイス間で一貫したエクスペリエンスを提供できます。

このドキュメントでは、Android アプリに「Google でログイン」の実装、[Google でログイン] ボタンの UI を設定する方法、アプリ向けに最適化されたワンタップの登録とログイン エクスペリエンスの構成について説明します。デバイスの移行をスムーズに行うために、「Google でログイン」は自動ログインをサポートしています。Android、iOS、ウェブ サーフェスはクロス プラットフォームであるため、どのデバイスでもアプリのログイン アクセスが可能です。

「Google でログイン」を設定するには、主に次の 2 つの手順を行います。

認証情報マネージャーのボトムシート UI のオプションとして「Google でログイン」を構成します。ユーザーにログインを自動的に求めるように設定できます。 パスキーまたはパスワードを実装している場合は、関連するすべての認証情報タイプを同時にリクエストできます。これにより、ユーザーは以前にログインに使用したオプションを覚える必要がなくなります。

認証情報マネージャーのボトムシート
図 1. 認証情報マネージャー ボトムシートの認証情報選択 UI

アプリの UI に [Google でログイン] ボタンを追加します。[Google でログイン] ボタンを使用すると、ユーザーは既存の Google アカウントを使用して Android アプリの登録やログインを簡単に行うことができます。ユーザーは、ボトムシートの UI を閉じた場合、または登録とログインに Google アカウントを明示的に使用する場合に、[Sign in with Google] ボタンをクリックします。デベロッパーにとっては、ユーザーのオンボーディングが容易になり、登録時の手間が軽減されます。

「Google でログイン」のフローを示すアニメーション
図 2. 認証情報マネージャーの [Google でログイン] ボタンの UI

このドキュメントでは、Google ID ヘルパー ライブラリを使用して、「Google でログイン」ボタンとボトムシート ダイアログを Credential Manager API と統合する方法について説明します。

Google API Console プロジェクトを設定する

  1. API Console でプロジェクトを開くか、まだ作成していない場合はプロジェクトを作成します。
  2. OAuth 同意画面のページで、すべての情報が完全かつ正確であることを確認します。
    1. アプリに正しいアプリ名、アプリのロゴ、アプリ ホームページが割り当てられていることを確認します。これらの値は、登録時の「Google でログイン」の同意画面とサードパーティ製のアプリやサービスの画面に表示されます。
    2. アプリのプライバシー ポリシーと利用規約の URL が指定されていることを確認します。
  3. まだ作成していない場合は、[認証情報] ページでアプリの Android クライアント ID を作成します。アプリのパッケージ名と SHA-1 署名を指定する必要があります。
    1. [認証情報] ページに移動します。
    2. [認証情報を作成] > [OAuth クライアント ID] をクリックします。
    3. アプリケーションの種類として [Android] を選択します。
  4. [認証情報] ページで、新しい「ウェブ アプリケーション」のクライアント ID を作成します(まだ作成していない場合)。現時点では、[承認済みの JavaScript 生成元] と [承認済みのリダイレクト URI] フィールドは無視してかまいません。このクライアント ID は、Google の認証サービスと通信する際にバックエンド サーバーを識別するために使用されます。
    1. [認証情報] ページに移動します。
    2. [認証情報を作成] > [OAuth クライアント ID] をクリックします。
    3. ウェブ アプリケーションの種類を選択します。

依存関係の宣言

モジュールの build.gradle ファイルで、最新バージョンの認証情報マネージャーを使用して依存関係を宣言します。

dependencies {
  // ... other dependencies

  implementation "androidx.credentials:credentials:<latest version>"
  implementation "androidx.credentials:credentials-play-services-auth:<latest version>"
  implementation "com.google.android.libraries.identity.googleid:googleid:<latest version>"
}

Google ログイン リクエストをインスタンス化する

実装を開始するには、Google ログイン リクエストをインスタンス化します。GetGoogleIdOption を使用して、ユーザーの Google ID トークンを取得します。

val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
  .setFilterByAuthorizedAccounts(true)
  .setServerClientId(WEB_CLIENT_ID)
  .setAutoSelectEnabled(true)
  .setNonce(<nonce string to use when generating a Google ID token>)
  .build()

まず、setFilterByAuthorizedAccounts パラメータを true に設定して API を呼び出して、以前にアプリへのログインに使用したアカウントをユーザーが持っているかどうかを確認します。ユーザーは、ログインに使用できるアカウントを選択できます。

承認済みの Google アカウントがない場合は、利用可能ないずれかのアカウントで登録するように求めるメッセージがユーザーに表示されます。これを行うには、API を再度呼び出し、setFilterByAuthorizedAccountsfalse に設定して、ユーザーにプロンプトを表示します。登録の詳細

リピーターの自動ログインを有効にする(推奨)

デベロッパーは、単一アカウントで登録するユーザーに対して自動ログインを有効にする必要があります。これにより、デバイス間でシームレスなエクスペリエンスが実現されます。特にデバイスの移行中、ユーザーは認証情報を再入力することなく、すばやくアカウントに再びアクセスできるようになります。これにより、すでにログインしていたユーザーにとっては、不必要な負担が解消されます。

自動ログインを有効にするには、setAutoSelectEnabled(true) を使用します。自動ログインは、次の条件を満たしている場合にのみ可能です。

  • リクエストに一致する認証情報は 1 つ(Google アカウントまたはパスワード)あり、この認証情報は Android 搭載デバイスのデフォルト アカウントと一致します。
  • ユーザーが明示的にログアウトしていない。
  • ユーザーが Google アカウントの設定で自動ログインを無効にしていない。
val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
  .setFilterByAuthorizedAccounts(true)
  .setServerClientId(WEB_CLIENT_ID)
  .setAutoSelectEnabled(true)
  .setNonce(<nonce string to use when generating a Google ID token>)
  .build()

ユーザーがアプリから明示的にログアウトした後、常に適切なアカウントを選択できるように、自動ログインを実装するときは必ずログアウトを処理してください。

ノンスを設定してセキュリティを強化する

ログイン セキュリティを強化し、リプレイ攻撃を回避するには、setNonce を追加して各リクエストにノンスを含めます。ノンスの生成の詳細

val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
  .setFilterByAuthorizedAccounts(true)
  .setServerClientId(WEB_CLIENT_ID)
  .setAutoSelectEnabled(true)
  .setNonce(<nonce string to use when generating a Google ID token>)
  .build()

「Google でログイン」のフローを作成する

「Google でログイン」のフローを設定する手順は次のとおりです。

  1. GetCredentialRequest をインスタンス化し、前に作成した googleIdOption を追加して認証情報を取得します。
  2. このリクエストを getCredential()(Kotlin)または getCredentialAsync()(Java)の呼び出しに渡して、ユーザーの利用可能な認証情報を取得します。
  3. API が正常に実行されたら、GoogleIdTokenCredential データの結果を保持する CustomCredential を抽出します。
  4. CustomCredential の型は GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL の値と同じである必要があります。GoogleIdTokenCredential.createFrom メソッドを使用して、オブジェクトを GoogleIdTokenCredential に変換します。
  5. 変換が成功したら、GoogleIdTokenCredential ID を抽出して検証し、サーバー上で認証情報を認証します。

  6. GoogleIdTokenParsingException での変換ができない場合は、「Google でログイン」ライブラリのバージョンの更新が必要になることがあります。

  7. 認識されないカスタム認証情報タイプを捕捉します。

val request: GetCredentialRequest = Builder()
  .addGetCredentialOption(googleIdOption)
  .build()

coroutineScope.launch {
  try {
    val result = credentialManager.getCredential(
      request = request,
      context = activityContext,
    )
    handleSignIn(result)
  } catch (e: GetCredentialException) {
    handleFailure(e)
  }
}

fun handleSignIn(result: GetCredentialResponse) {
  // Handle the successfully returned credential.
  val credential = result.credential

  when (credential) {

    // Passkey credential
    is PublicKeyCredential -> {
      // Share responseJson such as a GetCredentialResponse on your server to
      // validate and authenticate
      responseJson = credential.authenticationResponseJson
    }

    // Password credential
    is PasswordCredential -> {
      // Send ID and password to your server to validate and authenticate.
      val username = credential.id
      val password = credential.password
    }

    // GoogleIdToken credential
    is CustomCredential -> {
      if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
        try {
          // Use googleIdTokenCredential and extract id to validate and
          // authenticate on your server.
          val googleIdTokenCredential = GoogleIdTokenCredential
            .createFrom(credential.data)
        } catch (e: GoogleIdTokenParsingException) {
          Log.e(TAG, "Received an invalid google id token response", e)
        }
      } else {
        // Catch any unrecognized custom credential type here.
        Log.e(TAG, "Unexpected type of credential")
      }
    }

    else -> {
      // Catch any unrecognized credential type here.
      Log.e(TAG, "Unexpected type of credential")
    }
  }
}

「Google でログイン」ボタンのフローをトリガーする

[Google でログイン] ボタンのフローをトリガーするには、GetGoogleIdOption ではなく GetSignInWithGoogleOption を使用します。

val signInWithGoogleOption: GetSignInWithGoogleOption = GetSignInWithGoogleOption.Builder()
  .setServerClientId(WEB_CLIENT_ID)
  .setNonce(<nonce string to use when generating a Google ID token>)
  .build()

返された GoogleIdTokenCredential を、次のサンプルコードのように処理します。

fun handleSignIn(result: GetCredentialResponse) {
  // Handle the successfully returned credential.
  val credential = result.credential

  when (credential) {
    is CustomCredential -> {
      if (credential.type == GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL) {
        try {
          // Use googleIdTokenCredential and extract id to validate and
          // authenticate on your server.
          val googleIdTokenCredential = GoogleIdTokenCredential
            .createFrom(credential.data)
        } catch (e: GoogleIdTokenParsingException) {
          Log.e(TAG, "Received an invalid google id token response", e)
        }
      }
      else -> {
        // Catch any unrecognized credential type here.
        Log.e(TAG, "Unexpected type of credential")
      }
    }

    else -> {
      // Catch any unrecognized credential type here.
      Log.e(TAG, "Unexpected type of credential")
    }
  }
}

Google ログイン リクエストをインスタンス化したら、Google でログインのセクションで説明したのと同じ方法で認証フローを開始します。

新規ユーザーの登録を有効にする(推奨)

「Google でログイン」は、ユーザーがアプリやサービスで数回タップするだけで新しいアカウントを作成できる最も簡単な方法です。

保存されている認証情報が見つからない場合(getGoogleIdOption から Google アカウントが返されない場合)は、ユーザーに登録を促します。まず、setFilterByAuthorizedAccounts(true) をチェックして、以前に使用したアカウントが存在するかどうかを確認します。見つからない場合は、setFilterByAuthorizedAccounts(false) を使用して Google アカウントで登録するようユーザーに求めます。

例:

val googleIdOption: GetGoogleIdOption = GetGoogleIdOption.Builder()
  .setFilterByAuthorizedAccounts(false)
  .setServerClientId(SERVER_CLIENT_ID)
  .build()

Google の登録リクエストをインスタンス化したら、認証フローを開始します。ユーザーが Google への登録を望まない場合は、アカウント作成用の自動入力サービスまたはパスキーを検討してください。

ログアウトを処理する

ユーザーがアプリからログアウトしたら、API の clearCredentialState() メソッドを呼び出して、すべての認証情報プロバイダから現在のユーザー認証情報の状態を消去します。これにより、特定のアプリに保存されている認証情報セッションを消去する必要があることが、すべての認証情報プロバイダに通知されます。

認証情報プロバイダがアクティブな認証情報セッションを保存し、それを使用して今後の認証情報取得呼び出しのログイン オプションを制限している場合があります。たとえば、アクティブな認証情報を他の利用可能な認証情報よりも優先できます。ユーザーがアプリから明示的にログアウトし、次回から包括的なログイン オプションを取得するために、この API を呼び出して、保存されている認証情報セッションをプロバイダが消去できるようにする必要があります。