Android 用リンクされたアカウントへのログイン

リンク済みアカウント ログインを使用すると、サービスに Google アカウントをすでにリンクしているユーザーに対して Google でワンタップ ログインを有効にできます。これにより、ユーザー名とパスワードを再入力することなく、ワンクリックでログインできるため、ユーザー エクスペリエンスが向上します。また、ユーザーがサービスで重複するアカウントを作成する可能性も低くなります。

リンクされたアカウントによるログインは、Android のワンタップ ログイン フローの一部として利用できます。つまり、アプリでワンタップ機能がすでに有効になっている場合は、別のライブラリをインポートする必要はありません。

このドキュメントでは、リンクされたアカウントのログインをサポートするように Android アプリを変更する方法について説明します。

仕組み

  1. ワンタップ ログイン フローでリンク済みアカウントを表示するようにオプトインします。
  2. ユーザーが Google にログインしていて、Google アカウントをサービス上のアカウントにリンクしている場合、リンクされたアカウントの ID トークンが返されます。
  3. ユーザーに、リンクされたアカウントでサービスにログインするオプションを含む One Tap ログイン プロンプトが表示されます。
  4. ユーザーがリンクされたアカウントで続行することを選択すると、ユーザーの ID トークンがアプリに返されます。このトークンを、ステップ 2 でサーバーに送信されたトークンと照合して、ログインしたユーザーを識別します。

設定

開発環境をセットアップする

開発ホストに最新の Google Play 開発者サービスを入手します。

  1. Android SDK Manager を開きます。
  1. [SDK Tools] で [Google Play 開発者サービス] を見つけます。

  2. これらのパッケージのステータスが [インストール済み] でない場合は、両方を選択し、[パッケージをインストール] をクリックします。

アプリを構成する

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

    buildscript {
        repositories {
            google()
        }
    }
    
    allprojects {
        repositories {
            google()
        }
    }
    
  2. 「Link with Google」API の依存関係をモジュールのアプリレベルの Gradle ファイル(通常は app/build.gradle)に追加します。

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

リンクされたアカウントでのログインをサポートするように Android アプリを変更する

リンクされたアカウントのログイン フローの終了時に、ID トークンがアプリに返されます。ユーザーをログインさせる前に、ID トークンの完全性を確認する必要があります。

次のコードサンプルは、ID トークンを取得して確認し、その後ユーザーをログインさせる手順を示しています。

  1. ログイン インテントの結果を受け取るアクティビティを作成する

    Kotlin

      private val activityResultLauncher = registerForActivityResult(
        ActivityResultContracts.StartIntentSenderForResult()) { result ->
        if (result.resultCode == RESULT_OK) {
          try {
            val signInCredentials = Identity.signInClient(this)
                                    .signInCredentialFromIntent(result.data)
            // Review the Verify the integrity of the ID token section for
            // details on how to verify the ID token
            verifyIdToken(signInCredential.googleIdToken)
          } catch (e: ApiException) {
            Log.e(TAG, "Sign-in failed with error code:", e)
          }
        } else {
          Log.e(TAG, "Sign-in failed")
        }
      }
    

    Java

      private final ActivityResultLauncher<IntentSenderResult>
        activityResultLauncher = registerForActivityResult(
        new ActivityResultContracts.StartIntentSenderForResult(),
        result -> {
        If (result.getResultCode() == RESULT_OK) {
            try {
              SignInCredential signInCredential = Identity.getSignInClient(this)
                             .getSignInCredentialFromIntent(result.getData());
              verifyIdToken(signInCredential.getGoogleIdToken());
            } catch (e: ApiException ) {
              Log.e(TAG, "Sign-in failed with error:", e)
            }
        } else {
            Log.e(TAG, "Sign-in failed")
        }
    });
    
  2. ログイン リクエストを作成する

    Kotlin

    private val tokenRequestOptions =
    GoogleIdTokenRequestOptions.Builder()
      .supported(true)
      // Your server's client ID, not your Android client ID.
      .serverClientId(getString("your-server-client-id")
      .filterByAuthorizedAccounts(true)
      .associateLinkedAccounts("service-id-of-and-defined-by-developer",
                               scopes)
      .build()
    

    Java

     private final GoogleIdTokenRequestOptions tokenRequestOptions =
         GoogleIdTokenRequestOptions.Builder()
      .setSupported(true)
      .setServerClientId("your-service-client-id")
      .setFilterByAuthorizedAccounts(true)
      .associateLinkedAccounts("service-id-of-and-defined-by-developer",
                                scopes)
      .build()
    
  3. ログイン保留中のインテントを起動する

    Kotlin

     Identity.signInClient(this)
        .beginSignIn(
      BeginSignInRequest.Builder()
        .googleIdTokenRequestOptions(tokenRequestOptions)
      .build())
        .addOnSuccessListener{result ->
          activityResultLauncher.launch(result.pendingIntent.intentSender)
      }
      .addOnFailureListener {e ->
        Log.e(TAG, "Sign-in failed because:", e)
      }
    

    Java

     Identity.getSignInClient(this)
      .beginSignIn(
        BeginSignInRequest.Builder()
          .setGoogleIdTokenRequestOptions(tokenRequestOptions)
          .build())
      .addOnSuccessListener(result -> {
        activityResultLauncher.launch(
            result.getPendingIntent().getIntentSender());
    })
    .addOnFailureListener(e -> {
      Log.e(TAG, "Sign-in failed because:", e);
    });
    

ID トークンの完全性を確認する

Google API クライアント ライブラリを使用する

本番環境で Google ID トークンを検証する場合は、Java Google API クライアント ライブラリを使用することをおすすめします。

Java

  import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken;
  import com.google.api.client.googleapis.auth.oauth2.GoogleIdToken.Payload;
  import com.google.api.client.googleapis.auth.oauth2.GoogleIdTokenVerifier;

  ...

  GoogleIdTokenVerifier verifier = new GoogleIdTokenVerifier.Builder(transport, jsonFactory)
      // Specify the CLIENT_ID of the app that accesses the backend:
      .setAudience(Collections.singletonList(CLIENT_ID))
      // Or, if multiple clients access the backend:
      //.setAudience(Arrays.asList(CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3))
      .build();

  // (Receive idTokenString by HTTPS POST)

  GoogleIdToken idToken = verifier.verify(idTokenString);
  if (idToken != null) {
    Payload payload = idToken.getPayload();

    // Print user identifier
    String userId = payload.getSubject();
    System.out.println("User ID: " + userId);

    // Get profile information from payload
    String email = payload.getEmail();
    boolean emailVerified = Boolean.valueOf(payload.getEmailVerified());
    String name = (String) payload.get("name");
    String pictureUrl = (String) payload.get("picture");
    String locale = (String) payload.get("locale");
    String familyName = (String) payload.get("family_name");
    String givenName = (String) payload.get("given_name");

    // Use or store profile information
    // ...

  } else {
    System.out.println("Invalid ID token.");
  }

GoogleIdTokenVerifier.verify() メソッドは、JWT 署名、aud クレーム、iss クレーム、exp クレームを検証します。

ID トークンが Google Workspace または Cloud 組織アカウントを表すことを確認する必要がある場合は、Payload.getHostedDomain() メソッドから返されたドメイン名を確認することで、hd クレームを検証できます。