はじめに
Reactで作成したWebアプリケーションのユーザー認証部分をCognito + Amplifyフレームワークで構築してみました。
本記事では、Cognitoのユーザープールの作成方法、Reactでのコード実装についてまとめています。
完成画面
今回は、ユーザー名、パスワードとワンタイムパスワードの2段階認証を行います。そして、サインインをしたユーザーを識別し、トップページには「{ユーザー名}さんこんにちは!」という文字が出るようにします。
方法検討
要件
認証方法を考えるにあたり、条件は以下の通りです。
- 静的コンテンツをS3に置いている
- アプリケーション部分はLambdaで実装している
- アプリケーション内でユーザーを管理しており、ユーザーに登録されている人だけ使えるようにしたい
- 今後ユーザーのマイページも作りたいので、認証と合わせてユーザーを識別したい
- パスワードはシステム管理者が管理しないようにするのがベター
- ソーシャルアカウントは使わない
- MFA(多要素認証)を使いたい
結論
この条件に沿って認証部分を考えた結果、Cognitoを利用することにしました。また、Cognitoと既存のWebアプリケーションを簡単に連携できるAWSのAmplifyフレームワークというものがあったので、こちらも使ってみることにしました。
手順
下記の流れで進めていきます。
下記の1番から4番については別記事 構築準備編 へ。
- Reactの開発環境の構築
- Amplifyの設定
- Amplify用のIAMユーザーの作成
- Amplifyの初期設定
本記事ではこちらの手順についてまとめています。
5. Cognitoユーザープールの作成
6. コード実装
やってみる
5. Cognitoユーザープールの作成
前述の要件に沿ってユーザープールを作成していきます。
初めてのCognitoで何の設定をしているのかを調べるのが大変だったので1つ1つ残しておこうと思います。
Cognitoの作成を行うために下記のコマンドを実行します。
$ amplify add auth
詳細な設定を行うか、デフォルトの設定で良いか聞かれます。今回はMFAを使いたかったのでManual configuration
を選択しました。
# Do you want to use the default authentication and security configuration?
Manual configuration
今回はサインインが使えれば良かったので、User Sign-Up & Sign-In only (Best used with a cloud API only)
を選択しました。
# Select the authentication/authorization services that you want to use:
User Sign-Up & Sign-In only (Best used with a cloud API only)
リソース名とユーザープール名を入力します。
# Please provide a friendly name for your resource that will be used to label this category in the project:
test
# Please provide a name for your user pool:
test-cognito-mfa
ユーザー名とパスワードでサインインしたいので、Username
を選択します。この項目は、後から変更できません。
# How do you want users to be able to sign in?
Username
今回はグループは作成しないのでNo
を選択します。
# Do you want to add User Pool Groups?
No
ユーザープール内の情報を取得したり、ユーザーを登録したりするAPIを叩けるようにするかを選択します。今回は使わないのでNo
を選択します。
参考:https://docs.amplify.aws/cli/auth/admin
# Do you want to add an admin queries API?
No
今回はアプリケーション内でユーザーを管理していたため、このAPIを使わずに、自分でLambdaを作成しAWSのSDKを使ってユーザーの登録などを実装しましたが、このAPI使うのでも良かったかもしれないです。
MFAの設定を行います。今回はMFAを使いたいのでON
を選択します。この設定はあとで変えられないので気をつけましょう。そして、MFAにはAuthenticatorやAuthyなどのアプリケーションを用いてのワンタイムパスワードを使いたいので、TOTP
のみを選択しました。
# Multifactor authentication (MFA) user login options:
ON (Required for all logins, can not be enabled later)
# For user login, select the MFA types:
Time-Based One-Time Password (TOTP)
SMSのメッセージの設定です。
SMSは使わず、Eメールを使うつもりなので、デフォルトのままEnterします。
# Please specify an SMS authentication message:
Your authentication code is {####}
ユーザープールにはEメールアドレスも登録し、Eメールを使ってユーザーの登録やパスワードの再設定を行いたいのでEnabled
を選択します。
# Email based user registration/forgot password:
Enabled (Requires per-user email entry at registration)
認証コードを送るEメールの題名と本文の設定です。デフォルトのまま進みます。
# Please specify an email verification subject:
Your verification code
# Please specify an email verification message:
Your verification code is {####}
パスワードポリシーを設定します。
文字数を8文字以上、数字、特殊文字、大文字、小文字を含むパスワードのみしか設定できないようにしました。
# Do you want to override the default password policy for this User Pool?
Yes
# Enter the minimum password length for this User Pool:
8
# Select the password character requirements for your userpool:
Requires Lowercase, Requires Uppercase, Requires Numbers, Requires Symbols
ちなみに、Do you want to override the default password policy for this User Pool?
をNo
(= デフォルトのパスワード設定)とすると、8文字以上という条件しか設定されません。
サインアップ時に必要な属性を設定します。パスワードの再設定等にメールアドレスを使用するため、Email
を選択しました。この項目は、後から変更できません。
# What attributes are required for signing up?
Email
リフレッシュトークンはひとまずデフォルトの30日にしておきます。
# Specify the app's refresh token expiration period (in days):
30
サインアップはユーザー自身が行わず別の処理で行うので書き込み権限は付与しません。また、ログインしたユーザーの識別にはUsernameを使うので読み取り権限も付与しません。
# Do you want to specify the user attributes this app can read and write?
No
今回は、アプリケーション内で事前に登録しているユーザーのみしかログインできず、ユーザー自身がサインアップすることがないので追加しませんでしたが、下記のような機能を追加することもできます。
- Add Google reCaptcha Challenge
- Email Verification Link with Redirect
- Add User to Group
- Email Domain Filtering (blacklist)
- Email Domain Filtering (whitelist)
- Custom Auth Challenge Flow (basic scaffolding - not for production)
# Do you want to enable any of the following capabilities?
(選択なし)
今回はソーシャルログインは使わないのでOAuthはNo
を選択します。
# Do you want to use an OAuth flow?
No
CognitoをLambda関数のトリガーとして使う予定はなかったのでn
を入力。
例えば、ユーザーがサインアップしたことをトリガーとしてLambda関数を実行したいときなどに利用できます。
# Do you want to configure Lambda Triggers for Cognito?
No
これで構築するCognitoの設定が完了です。
そして、下記のコマンドで設定を保存します。
$ amplify push
環境やリソースがあっているかを聞かれるので、確認してy
を入力します。
# ? Are you sure you want to continue?
Yes
これでCognitoのユーザープールが作成されました!そしてsrc/aws-exports.js
にはid等が書き込まれます。
最後に、このままだとユーザー自身がサインアップできてしまうので、Cognitoコンソールからできないようにします。
先ほど作成したCognitoのユーザープールを開き、左のメニューから[ポリシー]をクリックし、[管理者のみにユーザーの作成を許可する]を選択します。その後、[変更の保存]をクリックし、保存してください。
Cognitoのコンソールからも設定を変更することもできますが、一部コンソールの変更では反映されないというのも見かけましたので、ご注意ください。Amplify CLIで設定を変更する場合は、$ amplify update auth
で設定を変更し、$ amplify push
を実行することで変更が保存されます。
6. コード実装
App.js
の中身を書き換えます。
参考:https://docs.amplify.aws/lib/auth/getting-started/q/platform/js
import React, {useEffect} from "react";
import Amplify, {Auth} from 'aws-amplify';
import awsconfig from './aws-exports';
import {withAuthenticator} from "@aws-amplify/ui-react";
Amplify.configure(awsconfig);
function App() {
const [currentUserName, setCurrentUserName] = React.useState("");
useEffect(() => {
const init = async() => {
const currentUser = await Auth.currentAuthenticatedUser();
setCurrentUserName(currentUser.username);
}
init()
}, []);
const signOut = async() => {
try {
await Auth.signOut();
} catch (error) {
console.log('error signing out: ', error);
}
document.location.reload();
}
return (
<div>
<h1>{currentUserName}さんこんにちは!</h1>
<button onClick={signOut}>サインアウト</button>
</div>
);
}
export default withAuthenticator(App);
実行結果
ユーザーの追加
ユーザーの追加を行います。まだ、アプリケーション内から登録ができるようになっていないので、今回はAWSのCognitoコンソールから追加を行います。
先ほど作成したユーザープール内の左メニュー[ユーザーとグループ]を選択し、[ユーザーの作成]をクリックします。
ユーザー名、ユーザーへの招待はEメールで行いたいのでEメールにチェック、電話番号は登録しないので検証済みのチェックを外し、Eメールアドレスを入力します。そして、[ユーザーの作成]をクリックし、ユーザーを作成します。ここで仮パスワードを未入力で登録すると、ユーザーにはランダムでパスワードが発行されます。
ユーザーが作成できました。
これでユーザーの登録は完了です。そしてユーザーには次のようなメールが送られます。
サインイン一連の流れ(2回目以降)
冒頭の完成画面をご覧ください。
※初回サインイン時には、パスワードの変更、TOTP用のQRコードを読み取る操作が加わります。
サインインページからアカウントは本当につくれないのか
最後にAWSコンソールからユーザーがサインアップできないように設定しましたが、本当にできないのかを試してみます。
エラーが出てサインイン画面からは登録できないことが確認できました!
おわりに
こんなに簡単にユーザー認証をつけることができました!画面もAmplifyのUIコンポーネントを使えば、こんなにも簡単に作成することができるんです!
ただ、このままではサインインページに使えないアカウント作成ボタンがあるので、そのボタンをなくしたい、AWSコンソールからではなくアプリケーションでユーザー管理できるようにしたいなど、要件に合うように多少カスタマイズも必要です。そのカスタマイズについて今後記事に残していければと思います。