[AWS CDK] Amazon CognitoでHosted UIを実装する際にハマったこと

2023.03.04

こんにちは、CX事業本部 Delivery部の若槻です。

Amazon CognitoではHosted UIを利用することにより、アプリケーションのユーザーサインインやサインアップに使用する認証画面を自前で実装せずにCognitoがホスティングする画面を使用することができます。

今回は、Amazon CognitoHosted UIをAWS CDKで実装する際にハマったことを共有します。

ハマったこと

URLにアクセスできない

lib/sample-stack.ts

import { aws_cognito, RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class SampleStack extends Stack {
  constructor(scope: Construct, id: string, props: StackProps) {
    super(scope, id, props);

    const userPool = new aws_cognito.UserPool(this, 'userPool', {
      userPoolName: 'userPool',
      selfSignUpEnabled: true,
    });

    userPool.addClient('userPoolClient', {
      userPoolClientName: 'userPoolClient',
      generateSecret: false,
      oAuth: {
        callbackUrls: ['https://dev.classmethod.jp/'], // DevelopersIOトップページ
        logoutUrls: ['https://classmethod.jp/'], // クラスメソッドトップページ
        flows: { authorizationCodeGrant: true },
        scopes: [
          aws_cognito.OAuthScope.EMAIL,
          aws_cognito.OAuthScope.PROFILE,
          aws_cognito.OAuthScope.OPENID,
        ],
      },
    });
  }
}

cdk deployでデプロイし、User poolおよびApp clientを作成します。

User poolに作成されたApp clientのコンソールを開き、Hosted UIの設定を見てみます。

するとHosted UI statusが無効(Unavailable)となっており、またHosted UIのURLが無効になっています。

原因、解決

User poolにドメインが設定されていないことが原因でした。

Hosted UIはCognitoにホストされた認証画面となるのですが、その際に使用されるドメインの指定が必要となっています。

AWS CDKではaddDomainでドメインを設定します。

Cognitoで設定できるドメインにはCognitoがホスティングするドメイン(Cognito domain)またはカスタムドメインが使えます。今回はCognito domainを設定してみます。

lib/sample-stack.ts

    userPool.addDomain('userPoolDomain', {
      cognitoDomain: { domainPrefix: 'uniqdomain' },
    });

cdk deployで変更をデプロイします。

Cognito domainが設定されました。

またHosted UI statusが有効 (Available)となり、またHosted UIのURLが有効になりました。

サインアップがエラーとなる

先程有効になったHosted UIのURLにアクセスしてみます。

サインイン画面が表示されました。

初回なのでまずはサインアップから行います。

メールアドレスとパスワードを指定して[Sign up]をクリック。

しかしエラー画面が表示されました。

An error was encountered with the requested page.

Verify(検証)画面に遷移し、指定したメールアドレスにVerify(検証)コードが記載されたメールが届くという期待動作となりませんでした。

ここでUser poolのユーザー一覧を見ると、ユーザーの登録はされていますが、Confirmation statusはUnconfirmed(未検証)となっています。

原因、解決

User poolのsignInAliasesemailを明示的に有効化する必要がありました。既定では無効となります。有効化することにより、ユーザーネームの代わりにメールアドレスをサインインおよびサインアップで使えるようになります。

signInAliasesは次のようにUser poolで設定をします。

lib/sample-stack.ts

    const userPool = new aws_cognito.UserPool(this, 'userPool', {
      userPoolName: 'userPool',
      selfSignUpEnabled: true,
      signInAliases: { email: true },
    });

注意点として作成済みのUser poolでsignInAliasesの設定を変更できないため、変更する場合はUser poolを再作成する必要があります。

先程と同様にHosted UIからサインアップをしてみると、今度は検証画面に遷移されました。

またメールアドレスに検証コードが届きました。

検証コードを使用して検証を完了するとリダイレクトURLに遷移しました。サインアップおよびサインインが成功したようです。

補足:autoVerifiedAttributesは自動で設定される

ちなみにUser poolでメールアドレスを検証に使用する場合は、autoVerifiedAttributesプロパティでemailが有効になっている必要があります。

しかしCDK(TypeScript)のドキュメントを見ると、signInAliasで有効にされている検証方法は自動的にautoVerify(autoVerifiedAttributes)で有効になるとのことです。

Attributes which Cognito will look to verify automatically upon user sign up. EMAIL and PHONE are the only available options.

If signInAlias includes email and/or phone, they will be included in autoVerifiedAttributes by default. If absent, no attributes will be auto-verified.

ちなみにCLIでUser poolを設定する際にはautoVerifiedAttributesを明示的に有効化する必要があり、以前その部分でハマりました。(下記ブログ参照)

最終的に動いたコード

lib/sample-stack.ts

import { aws_cognito, RemovalPolicy, Stack, StackProps } from 'aws-cdk-lib';
import { Construct } from 'constructs';

export class SampleStack extends Stack {
  constructor(scope: Construct, id: string, props: StackProps) {
    super(scope, id, props);

    const userPool = new aws_cognito.UserPool(this, 'userPool', {
      userPoolName: 'userPool',
      selfSignUpEnabled: true,
      signInAliases: { email: true },
    });

    userPool.addClient('userPoolClient', {
      userPoolClientName: 'userPoolClient',
      generateSecret: false,
      oAuth: {
        callbackUrls: ['https://dev.classmethod.jp/'], // DevelopersIOトップページ
        logoutUrls: ['https://classmethod.jp/'], // クラスメソッドトップページ
        flows: { authorizationCodeGrant: true },
        scopes: [
          aws_cognito.OAuthScope.EMAIL,
          aws_cognito.OAuthScope.PROFILE,
          aws_cognito.OAuthScope.OPENID,
        ],
      },
    });
  }

  userPool.addDomain('userPoolDomain', {
    cognitoDomain: { domainPrefix: 'uniqdomain' },
  });
}

以上