For

2023.3.5

Next.js + CognitoでHosted UIを使わずに自前のUIからサインイン、サインアップ、AuthGuardなどの認証周りの挙動を実現するサンプルアプリを公開しました

概要

Cognitoを利用する際にHosted UIを使うと認証関連の機能と画面を簡単に提供できますが、プロダクトとして認証周りの画面も統一感のある自前の画面を提供したいことがあると思います。
その場合に、機能面では@aws-amplify/authライブラリを使い、画面としてはNext.js(React)で構成された画面に組み込みが必要となるため、実際に組み込んでみたときの気付きなどをまとめました。

また、ソースコードとしては サンプルリポジトリ を公開しました。

Cognitoユーザープールの作成

2023/03/05時点でのCognitoユーザープール作成画面を例として進めていきます。

サインインエクスペリエンス


セキュリティ要件

パスワードのポリシーはお好みで変更してください。
なお、概要にリンクを付けたサンプルリポジトリでは下記のパスワードポリシーに対するバリデーションをフロントエンドでつけています。


サインアップエクスペリエンス




メッセージ配信


アプリケーションを統合

ここでは「CognitoのホストされたUIを使用」のチェックが外れていることを確認します。


また「クライアントシークレットを生成しない」が選択されていることを確認します。
クライアントシークレットを生成するかどうかはあとから変更できない項目であり、クライアントシークレットが生成される設定になっていると今回利用する @aws-amplify/auth が利用できなくなってしまうので必ず確認します。

@aws-amplify/authの機能の確認

細かい実装などは概要にもリンクをつけた サンプルリポジトリ から確認できるとして、ここでは機能の確認をしていきます。
@aws-amplify/auth が提供するメソッドを自前のUIから呼び出すことになります。

今回のサンプルリポジトリで使用したメソッドは以下のとおりです。

・currentAuthenticatedUser
・signUp
・confirmSignUp
・resendSignUp
・completeNewPassword
・signIn
・forgotPassword
・forgotPasswordSubmit
・signOut

getUserAttributesは非同期っぽいけどawaitできない

CognitoUserオブジェクトには、そのユーザーの attributes を取得するためのメソッド getUserAttributes が用意されています。
このメソッドはPromiseが返ってこないのでawaitできませんが、例えば以下のように実装すると getUserAttributes 以降の処理が先に実行されてしまいます。

TypeScript_____sample_____cognitoUser.getUserAttributes((_, attributes) => {
  const { email, email_verified } = Object.fromEntries(
    attributes!.map(({ Name, Value }) => [Name, Value])
  );
  userAttributes = { email, email_verified }
  console.log(userAttributes);
})

console.log('This line should be called after getUserAttributes'); // <- この行が先に呼び出される


つまり getUserAttributes したあとに、取得した attributes を使用する処理は getUserAttributes の完了を待ってから実行する必要があります。
サンプルリポジトリではその点についても考慮されています。

signUpとcompleteNewPassword

上記のメソッドについては基本的に必要なメソッドを必要なときに呼び出して、エラーハンドリングは適宜行うだけではありますが、この中でsignUp関連で少しだけポイントがあります。
上記メソッドから、signUp、confirmSignUpを通して作成されたアカウントについてはsingInしたときのレスポンスであるCognitoUserオブジェクトのプロパティとして attributes が存在し、ユーザーのemail、email_verifiedなど属性値が参照できます。
ただ、AWSコンソール画面から作成されたユーザーについては、signInしたときのレスポンスに attributes プロパティが存在せず challengeName というプロパティが存在します。
このプロパティを参照すると NEW_PASSWORD_REQUIRED というテキストがセットされており、このユーザーのステータスとして新しいパスワードを設定しなければならないことがわかります。

この確認にひとつ上の項目でとりあげた getUserAttributes の結果を利用します。
getUserAttributes はCognitoUserオブジェクトに存在する attributes プロパティの値が取得できます。まだこのプロパティの値を取得できないユーザーについては null が返ってきますのでその場合には completeNewPassword を使って新しいパスワードを設定してもらいます。
こうすることで次回からこのユーザーはCognitoUserオブジェクトから attributes プロパティの値を取得できるようになります。

まとめ

今回の記事では @aws-amplify/auth を使ってCognitoのHosted UIを使用せずに自前の画面から認証機能を提供する際の気にするポイントをまとめました。
Cognitoの認証機能を提供したい場合の参考になれば幸いです。