【React】ユーザー認証をCognito + Amplifyで構築してみた ~ 構築完成編 ~


はじめに

Reactで作成したWebアプリケーションのユーザー認証部分をCognito + Amplifyフレームワークで構築してみました。
本記事では、Cognitoのユーザープールの作成方法、Reactでのコード実装についてまとめています。

完成画面

今回は、ユーザー名、パスワードとワンタイムパスワードの2段階認証を行います。そして、サインインをしたユーザーを識別し、トップページには「{ユーザー名}さんこんにちは!」という文字が出るようにします。

方法検討

要件

認証方法を考えるにあたり、条件は以下の通りです。

  • 静的コンテンツをS3に置いている
  • アプリケーション部分はLambdaで実装している
  • アプリケーション内でユーザーを管理しており、ユーザーに登録されている人だけ使えるようにしたい
  • 今後ユーザーのマイページも作りたいので、認証と合わせてユーザーを識別したい
  • パスワードはシステム管理者が管理しないようにするのがベター
  • ソーシャルアカウントは使わない
  • MFA(多要素認証)を使いたい

結論

この条件に沿って認証部分を考えた結果、Cognitoを利用することにしました。また、Cognitoと既存のWebアプリケーションを簡単に連携できるAWSのAmplifyフレームワークというものがあったので、こちらも使ってみることにしました。

手順

下記の流れで進めていきます。

下記の1番から4番については別記事 構築準備編 へ。
1. Reactの開発環境の構築
2. Amplifyの設定
3. Amplify用のIAMユーザーの作成
4. 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)

参考(左メニュー [MFAそして確認] 内)

認証コードを送る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

App.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コンソールからではなくアプリケーションでユーザー管理できるようにしたいなど、要件に合うように多少カスタマイズも必要です。そのカスタマイズについて今後記事に残していければと思います。