CognitoでGoogleアカウントとFacebookアカウントを紐づける


目的

CognitoでGoogleアカウントとFacebookアカウントを紐づけることで、メールアドレスが同一なら同一ユーザであると識別する。

ユーザプール設定

まずはAWSコンソール上で全ての設定をデフォルトでユーザプールを作成します。
その後、IDプロバイダーの設定でFacebook・Googleを追加します。
追加の方法はこちらのページで詳しく説明されています。
設定し終わると以下のような設定になります。

その後、アプリクライアントの設定タブで有効なIDプロバイダにチェックを入れ、コールバックURLの設定をします。

一番下にあるホストされたUIを起動をクリックすると以下の画面が現れます。

Google・Facebookそれぞれのアカウントでログインして以下のことが確認できれば設定は完了です。

  • ちゃんとコールバックする
  • ユーザとグループタグでユーザが作成されている

実装

以下が実際に試したコードの一部になります。

    public function snsSignIn(string $authorizationCode) // コールバックで受け取った code
    {
        $signInResponse = $this->cognito->snsSignIn($authorizationCode); // ※1
        try {
            // DB の中に Cognito のユーザネームが保存されていればログイン、なければ 404 になります
            $this->authentication->login($signInResponse->getCognitoTokens());
        } catch (\Exception $exception) {
            if ($exception->getCode() !== 404) {
                throw $exception;
            }

            $accessToken = $signInResponse->getAccessToken();
            $socialUser = $this->cognito->fetchUserInfo($accessToken); // ※2
            $cognitoUsername = $socialUser['email'];
            $socialAccountUsername = $socialUser['username'];

            try {
                $cognitoUser = $this->cognito->adminGetUser($cognitoUsername); // ※3
                $cognitoId = $cognitoUser['Username'];
            } catch (Exceptions\UserNotFoundException $exception) {
                // Cognitoアカウントを作成し、ステータスを CONFIRM にする。
                $createResponse = $this->cognito->createUser($cognitoUsername);
                $this->cognito->adminSetUserPassword($cognitoUsername);
                $cognitoId = $createResponse->getCognitoId();
                // TODO DB に Cognito ユーザネームを登録
            }

            // Status が EXTERNAL_PROVIDERであるソーシャルアカウントを削除
            $this->cognito->adminDeleteUser($socialAccountUsername); // ※4

            // Cognitoアカウントとソーシャルアカウントをリンク
            $this->cognito->linkProviderForUser($cognitoId, $socialAccountUsername); // ※5
        }
        $this->accessTokenHandler->setUserPool($this->cognito);
        return route('HomeIndex');
    }

※1 トークンエンドポイント
※2 USERINFO エンドポイント
※3 AdminGetUser
※4 AdminDeleteUser
※5 AdminLinkProviderForUser

注意すべき点は※4の部分です。
linkProviderForUserはソーシャルアカウントと既存のユーザを紐づけることができますが、紐づけたいソーシャルアカウントが既にEXTERNAL_PROVIDERでアカウントが生成されている場合は以下のエラーが出てしまいます。
Merging is not currently supported, provide a SourceUser that has not been signed up in order to link

そのためリンクする前にEXTERNAL_PROVIDERのアカウントを削除しています。

上記のコードを使用し、FacebookとGoogleアカウントでログインしたあとのCognitoの状態が以下の画面です。

アカウントが一つだけ存在します。

中身を見てみるとIdentitiesにGoogleとFacebookのアカウントが登録されていることがわかります。