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

選択可能な各種 ID が表示された認証情報マネージャー対応のボトムシート。
図 1. Credential Manager API との統合後の「Google でログイン」ボトムシートのダイアログ表示

このドキュメントでは、Google ID ヘルパー ライブラリを使用して、「Google でログイン」ボトムシート ダイアログを Google Identity Services(GIS)から認証情報マネージャー API へと移行させる方法を説明します。

認証情報マネージャー API を使用するアプリは、一貫性のある Android ユーザー インターフェースをエンドユーザーに提示するよう構成されています。これにより、ユーザーはパスキー対応アカウントなどの保存済みログイン オプションのリストから選択できます。これは、さまざまな認証情報タイプとプロバイダを統合する際に推奨される Android API です。Android 14 以降では、認証情報マネージャー API を使用してサードパーティのパスワード マネージャーも利用できます。

依存関係の宣言

モジュールの 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 API Console プロジェクトを設定します。依存関係の追加に関するガイダンスは、上記の手順に置き換えてください。

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

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

Kotlin

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

Java

GetGoogleIdOption googleIdOption = new GetGoogleIdOption.Builder()
  .setFilterByAuthorizedAccounts(true)
  .setServerClientId(SERVER_CLIENT_ID)
  .build();

最初に、setFilterByAuthorizedAccounts パラメータを true に設定して API を呼び出す必要があります。使用可能な認証情報がない場合は、API を再度呼び出し、setFilterByAuthorizedAccountsfalse に設定します。

可能であれば、ユーザーを自動的にログインさせる場合は、GetGoogleIdOption リクエストで setAutoSelectEnabled を使用してこの機能を有効にします。次の条件が満たされている場合、自動ログインが可能です。

  • ユーザーはアプリの認証情報を 1 つだけ保存している。つまり、保存したパスワードまたは Google アカウントが 1 つだけある。
  • ユーザーが Google アカウント設定で自動ログインを無効にしていない。

ログイン セキュリティを向上させ、リプレイ攻撃を回避するには、setNonce を使用して各リクエストにノンスを含めます。詳しくは、ノンスの生成についてをご覧ください。

Kotlin

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

Java

GetGoogleIdOption googleIdOption = new GetGoogleIdOption.Builder()
  .setFilterByAuthorizedAccounts(true)
  .setServerClientId(SERVER_CLIENT_ID)
  .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. 認識されないカスタム認証情報タイプを捕捉します。

Kotlin

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) {
    is PublicKeyCredential -> {
      // Share responseJson such as a GetCredentialResponse on your server to
      // validate and authenticate
      responseJson = credential.authenticationResponseJson
    }

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

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

Java

GetCredentialRequest request = new GetCredentialRequest.Builder()
  .addGetCredentialOption(googleIdOption)
  .build();

// Launch sign in flow and do getCredential Request to retrieve the credentials
credentialManager.getCredentialAsync(
  requireActivity(),
  request,
  cancellationSignal,
  <executor>,
  new CredentialManagerCallback<GetCredentialResponse, GetCredentialException>() {
    @Override
    public void onResult(GetCredentialResponse result) {
      handleSignIn(result);
    }

    @Override
    public void onError(GetCredentialException e) {
      handleFailure(e);
    }
  }
);

public void handleSignIn(GetCredentialResponse result) {
  // Handle the successfully returned credential.
  Credential credential = result.getCredential();

  if (credential instanceof PublicKeyCredential) {
    String responseJson = ((PublicKeyCredential) credential).getAuthenticationResponseJson();
    // Share responseJson i.e. a GetCredentialResponse on your server to validate and authenticate
  } else if (credential instanceof PasswordCredential) {
    String username = ((PasswordCredential) credential).getId();
    String password = ((PasswordCredential) credential).getPassword();
    // Use id and password to send to your server to validate and authenticate
  } else if (credential instanceof CustomCredential) {
    if (GoogleIdTokenCredential.TYPE_GOOGLE_ID_TOKEN_CREDENTIAL.equals(credential.getType())) {
      try {
        // Use googleIdTokenCredential and extract id to validate and
        // authenticate on your server
        GoogleIdTokenCredential googleIdTokenCredential = GoogleIdTokenCredential.createFrom(((CustomCredential) credential).getData());
      } catch (GoogleIdTokenParsingException e) {
        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 でログイン] ボタンは、最新の Google ID ヘルパー ライブラリを使用する認証情報マネージャーでサポートされています。[Google でログイン] ボタンのフローをトリガーするには、GetGoogleIdOption の代わりに GetSignInWithGoogleOption を使用し、返された GoogleIdTokenCredential を以前と同じ方法で処理します。

Google で登録

GetGoogleIdOption リクエストをインスタンス化して GetCredentialsRequest に渡す際に、setFilterByAuthorizedAccountstrue に設定しても結果が返されない場合は、ログインするための承認済みアカウントがないことを示しています。この時点で、setFilterByAuthorizedAccounts(false) を設定して、Google で登録を呼び出す必要があります。

Kotlin

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

Java

GetGoogleIdOption googleIdOption = new GetGoogleIdOption.Builder()
  .setFilterByAuthorizedAccounts(false)
  .setServerClientId(SERVER_CLIENT_ID)
  .build();

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

ログアウトを処理する

ユーザーがアプリからログアウトしたら、API の clearCredentialState() メソッドを呼び出して、現在のユーザー認証情報の状態を消去し、ログインの内部状態をリセットします。