🟢

Amazon Cognito UserPoolでメールアドレスが事前登録されたユーザーにだけソーシャル・ログインを許可する仕組みを作る

2021/07/10に公開

はじめに

AWSが提供するエンドユーザー向けの認証のサービスであるCognoto UserPoolを使えば簡単にID&パスワードを使った認証の仕組みを作ることができます。UserPoolはエンドユーザーの自己サインアップの許可/不許可を設定することが可能で、提供するソフトウエアの要件に応じて自由に設定することが可能です。
また、UserPoolはソーシャル・ログインに対応しておりFacebook・Google・Amazon・AppleまたはSAML・OpenID Connectに対応しているIDプロバイダーをサポートしています。
しかし、自己サインアップ不許可 & ソーシャル・ログインは単純な設定だけでは実現ができません。(不許可の設定はできるが、ソーシャルアカウントでサインアップが出来てしまう)

この要件をCognito Lambdaトリガーというユーザーのサインアップ・サインイン時に任意の処理をLambda関数を使って差し込める機能を使って実現したのでその方法を紹介します。ソフトウエアの要件によっては削除せずに、サインアップされた日時を保存しておくのも良さそうです。

Cognito Lambdaトリガー

今回はエンドユーザーのメールアドレスが登録されていない場合にそもそもUserPoolへの登録を拒否したいので、サインアップ前(Pre SignUp)トリガーを使用します。

Lambda関数では下記のような処理を行います。

import {
  DeleteItemCommand,
  DeleteItemInput,
  DynamoDB,
  GetItemCommand,
  GetItemInput,
} from '@aws-sdk/client-dynamodb';

interface SignUpEvent {
  request: {
    userAttributes: {
      [key: string]: string;
    };
    validationData: {
      [key: string]: string;
    };
    clientMetadata: {
      [key: string]: string;
    };
  };
  response: {
    autoConfirmUser: boolean;
    autoVerifyPhone: boolean;
    autoVerifyEmail: boolean;
  };
}

const ddb = new DynamoDB({});

export const handler = async (event: SignUpEvent) => {
  // 1. メールアドレスを取得する
  const email = event.request.userAttributes['email'];

  // 2. データベースにメールアドレスが登録されているか確認する
  const {Item} = await ddb.send(
    new GetItemCommand({
      TableName: process.env.TABLE_NAME!,
      Key: {email: {S: email}},
    })
  );

  // 3. 登録されていない場合はサインアップを拒否する
  if (Item === undefined) {
    throw new Error('this email address is not allowed to sign up');
  }

  // 4. 事前登録データを削除する
  await ddb.send(
    new DeleteItemCommand({
      TableName: process.env.TABLE_NAME!,
      Key: {email: {S: email}},
    })
  );

  // 5. リクエストを改変せずに処理を継続させる
  return event;
};

ここで設定した例外のメッセージはサインアップに失敗しブラウザへリダイレクトで戻る際にQueryStringに設定され、JavaScriptで取得が可能です。
(例) http://localhost:3000/?error_description=PreSignUp+failed+with+error+this+email+address+is+not+allowed+to+sign+up.+&state=****************&error=invalid_request#

DynamoDBテーブルは単純でemailを唯一のAttributeかつプライマリキーとしてメールアドレスを保存します。今回はテーブルへのメールアドレス登録はマネジメントコンソールから行ってしまいます。

構成図

メールアドレスの事前登録情報を保存するにはDynamoDBを使用します。サインアップ前トリガーは、サインアップ時にのみトリガーされるので登録が確認できたらアイテムを削除します。サービス

UserPoolの設定

メールアドレスを取得するために、属性マッピング設定でGoogleアカウントにおけるemail属性をUserPoolにおけるEmailとマッピングさせる必要があります。

この設定をしないと、サインアップ前Lambdaトリガーでユーザーのメールアドレスを取得できないので設定を忘れないようにしてください。

今回使用したコード

今回の構成はAWS CDKとReact(Create React App)を使用して構築しました。GitHubに公開しているので、再現したい場合や具体的な設定を確認する場合にご使用ください。

intercept6/cognito-up-pre-registration-with-social-signin

あとがき

今回はメールアドレスの事前登録を必須とする為にLambdaトリガーを使用しましたが、もっとシンプルに特定のドメインだけど許可するなど様々な要件に適用できそうです。

Discussion