6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Cognitoでメールアドレス認証ユーザとGoogle認証ユーザを統合する方法

Last updated at Posted at 2023-05-05

目次

はじめに

Cognitoにて異なる認証方式を用いてサインアップした同一ユーザを、メールアドレスでマッピングする方法を記載します。

Cognitoではメールアドレスとパスワードを用いた認証以外に、GoogleやFacebookのアカウントを用いた認証も可能となっております。これは便利ではありますが、以下のような疑問点が出てくるのではないでしょうか。

疑問点:既にメールアドレスとパスワードを用いてサインアップを行なっているユーザAが存在したとして、ユーザAがGoogleを使用してログインした場合に、ユーザ情報は別になるのか。

こちらについての結論は以下になります。

結論:Cognitoでは認証方法が違う限り、メールアドレスが同じでも別々のユーザとして扱う。

これでは同じユーザが2つ以上のアカウントを持ってしまうことに繋がるため、今回はLambdaを使って、異なる認証方式を用いて認証を行なった同一メールアドレスを持つアカウントを統合する方法を記載します。

この記事で扱うこと

  • サインアップ前のLambdaトリガーを使用したユーザの統合方法
    • 既にEmailで認証済みのユーザがGoogle認証を行なった際に、同一ユーザとしてログイン出来るようにする方法

この記事で扱わないこと

  • Cognitoユーザプールの作成方法
  • CognitoユーザプールへのGoogle認証の追加方法
  • Lambdaの詳細説明
  • 既にGoogleで認証済みのユーザがEmail認証を行なった際に、同一ユーザとしてログイン出来るようにする方法(重要)

前提条件

既存ユーザとGoogle認証ユーザを統合する方法

※記事の紹介でも触れましたが、今回の内容は「既にEmailで認証済みのユーザがGoogle認証を行なった際に、同一ユーザとしてログイン出来るようにする方法」であり、その逆の方法は記載しません。

全体の流れをざっくり説明

イメージを掴んでいただくために処理フローを記載します。
SNS認証をした際のCognito内のフローは通常以下のようになります。
※厳密には認可コードのやりとりなどが発生するため、もっと複雑な処理になりますがここでは一旦省略します。
スクリーンショット 2023-05-05 15.53.12.png

そして今回はこのようなフローを実現させることを目的とします。
スクリーンショット 2023-05-05 16.05.10.png

Lambdaにて黄色く塗りつぶしている箇所を実装し、サインアップ前のLambdaトリガーにて発火させることで、このフローを実現します。

サインアップ前のLambdaトリガーの詳細はこちらから
https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/user-pool-lambda-pre-sign-up.html

Lambdaの実装手順

1.Lambdaを検索、「関数の作成」をクリック

Lambda_1.png

2.関数の作成

今回は一から作成を選択し、言語はPython3.10、他は一旦全てデフォルトのままにして作成します。

Lambda_2.png

3.関数の中身を記載し、「デプロイ」を実行

Lambda3.png

コードは以下の通りです。

cognitoIDMergeFunc.py
import json
import boto3

cognito_client = boto3.client('cognito-idp')

#対象のユーザープールIDを記載
user_pool_id = 'your_user_pool_id'

def lambda_handler(event, context):
    # ユーザープールに追加しているidp一覧
    idp_list = ['Google','Facebook']

    # SNS認証を使った場合、idpの名前の一部がuserNameから取得出来る(userName = google_xxxxyyyyy)
    # idpの名前が入っていなければSNS認証ユーザではないため、以降の処理を実施しない
    user_name = event['userName'].split('_')
    user_idp = ''
    for idp in idp_list:
        if idp.lower() == user_name[0]:
            user_idp = idp
    if user_idp == '':
        return event
    
    # ユーザープール内に同一メールアドレスを持つユーザがいないか検索をかける
    # list_usersに渡す検索文を作成する
    email_address = event['request']['userAttributes']['email']
    filter_str = 'email = ' + '"' + email_address + '"'

    # list_usersでユーザープール内のユーザを参照する
    # Filterとしてemailを指定する
    same_user = ''
    same_user = cognito_client.list_users(
            UserPoolId = user_pool_id,
            Filter = filter_str
    )
    
    # もし同一メールアドレスを使用したユーザがいなければ、統合処理は行わずユーザの新規作成を行う
    if same_user == '':
        return event
    
    # 同一メールアドレスを使用しているユーザがいれば、AdminLinkProviderForUserを使用してユーザ情報を統合する
    # Googleの場合ProviderAttributeValueは、「google_XXXXXXYYYYYY」の「XXXXXXYYYYYY」となる
    user_id = user_name[1]
    response = cognito_client.admin_link_provider_for_user(
        UserPoolId=user_pool_id,
        DestinationUser={
            'ProviderName': 'Cognito',
            'ProviderAttributeValue': same_user['Users'][0]['Username']
        },
        SourceUser={
            'ProviderName': user_idp,
            'ProviderAttributeName': 'Cognito_Subject',
            'ProviderAttributeValue': user_id
        }
    )
    
    return event

ListUsersについて

APIドキュメント
https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_ListUsers.html

boto3ドキュメント
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cognito-idp/client/list_users.html

AdminLinkProviderForUsersについて

APIドキュメント
https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_AdminLinkProviderForUser.html

boto3のドキュメント
https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/cognito-idp/client/admin_link_provider_for_user.html

4.Lambdaのロールの設定変更

デフォルトの設定だとAdminLinkProviderForUsersやListUsersが実行できないため、実行ロールに関数の実行権限を与える必要があります。

Lambda4.png

5. 設定画面にて「インラインポリシーを作成」を選択

Lambda5.png

6.関数の許可設定を行う

  • サービスは「Cognito User Pools」
  • アクションとして「ListUsers」と「AdminLinkProviderForUsers」を選択
    Lambda6.png

7.ポリシー名を設定し、「ポリシーの作成」を選択

Lambda7.png

8.ポリシーを確認

許可ポリシー欄に作成したポリシーがあることを確認し完了です。
Lambda8.png

Cognitoの設定手順

1.ユーザープールの設定画面から「Lambdaトリガーを追加」を選択

cognito_lambda1.png

2. サインアップ前トリガーにて、先ほど作成したLambda関数を割り当てる

関数を割り当てて保存したら完了です。

cognito_lambda2.png

動作確認

Lambdaを使用しないデフォルト状態

まずデフォルトの状態がどうなるかを事前にお見せします。
こちらの画像の通り、同一メールアドレスを持つユーザだとしても、ユーザプールでは別々のユーザとして扱われます。
chack_1_standard.png

ユーザの詳細はこちらになります。
chack_1_cognito.png
check1_google.png

Lambda関数使用後

こちらがLambda作成後です。
ユーザデータが一つであることが確認できます。
check2_index.png

Lambdaにより、Email認証ユーザの「Identities」内にGoogleから取得した情報が保存されました。
これにてユーザの統合が完了です。
check_2_user_sub.png

ログインも両方のアカウントで完了すること、ID Token内のSubjectがemailログイン時とGoogleログイン時で同じになることが確認できました。
スクリーンショット 2023-05-05 18.17.07.png

まとめ

以上がCognitoでEmail認証ユーザとGoogle認証ユーザのアカウントを統合する方法でした。
AWSやPythonに明るいわけではないためベストプラクティスでない部分も多々あると思いますが、誰かのお役に立てれば幸いです。

6
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?