みなさんこんにちは。広野です。
サービス提供中の React アプリでサインイン機能の機能追加要件があったので、その機会にサインイン UI のアップグレードも検証しました。
サインイン UI には Amplify UI を利用していまして、昨年メジャーバージョンアップ(v1 -> v2)されていました。
バージョンアップによりだいぶコードが変わっていたので、コードの書き換えに少々苦労しました。公式ドキュメントだけでは動く状態まで到達できなかったので、エラー回避も含め、Amplify UI v2 を使って動いたReactコードを紹介したいと思います。
やったこと
単にサインイン UI パーツを変えただけです。
以下の図のように、ユーザにとっては見た目が変わっただけで、サインイン操作は変わりません。
Amplify UI のデフォルトデザインを使っていただけなので、何の変哲もありません。
デザインに変更がありましたが、こだわりはないのでそのまま使っています。
使用サービス・バージョン
AWSサービス
AWS Amplify
Amazon Cognito
Reactモジュール
react v17.0.2
aws-amplify v4.3.15
@aws-amplify/ui-react v2.8.0
動いたコード・解説
ユーザが最初にアクセスする画面 (App.js)
アプリにアクセスしたときに最初に通過する、root 的な位置にある画面です。
ここのコードは Amplify UI の公式ドキュメントに従っていますが、自分が理解しやすいように書き換えました。
Amplify CLI は一切使用していません。そのため Cognito 連携用設定を Amplify 環境変数から取得しています。
import React from 'react'; import Amplify from 'aws-amplify'; import { Authenticator, useAuthenticator } from '@aws-amplify/ui-react'; import Loggedin from './Loggedin'; //ログイン後ホーム画面 import Notloggedin from './Notloggedin'; //ログイン前画面(サインイン画面) //Amplify Cognito 連携設定 Amplify.configure({ Auth: { region: process.env.REACT_APP_REGION, userPoolId: process.env.REACT_APP_USERPOOLID, userPoolWebClientId: process.env.REACT_APP_USERPOOLWEBCLIENTID } }); //ログインチェック //ユーザがアクセスしたときにトークンを既に持っている(ログイン済みか)をチェックします。 //ログイン済みなら、routeという変数に authenticated という文字列を返すので、 //ログイン済みなら <Loggedin /> のページを、未ログインなら <NotLoggedin /> のページを呼びます。 function Authcheck() { const { route } = useAuthenticator((context) => [context.route]); return (route === "authenticated") ? <Loggedin /> : <Notloggedin />; } //ログインチェックの関数を、<Authenticator.Provider>タグでラップします。 export default function App() { return ( <Authenticator.Provider> <Authcheck /> </Authenticator.Provider> ); }
未ログイン時画面 (NotLoggedin.js)
<Authenticator /> が Amplify UI 提供のログインUIになります。このコードでは hideSignUp オプションを true にすることで、セルフサインアップの画面を表示しないようにしています。
import React from 'react'; import { Authenticator } from '@aws-amplify/ui-react'; import '@aws-amplify/ui-react/styles.css'; //この記述の通りCSSを読み込ませないとデザインが崩れます。 //ログインしていないときの画面 const Notloggedin = () => { return ( <div id="page-wrapper"> {/* Main */} <div id="main"> <div className="container"> <div className="row main-row"> <div className="col-12"> <Authenticator hideSignUp={true} /> </div> </div> </div> </div> </div> ); }; export default Notloggedin;
ログイン後画面 (Loggedin.js)
ログインが成功すると、ユーザはこちらの画面に誘導されます。
最初に、公式ドキュメント通りにログイン済みのユーザ情報を取得するコードを実行します。
user にはログインしたユーザの情報が格納され、signOut にはサインアウト用の関数が格納されます。
- ユーザ名: user.username
- IDトークン: user.signInUserSession.idToken.jwtToken
- サインアウト用関数: signOut
これらの値は Cognito からデータを受け取っています。Amplify UI v1 と v2 で受け取り方の仕様変更がなかったので助かりました。
ここではサンプルとしてホーム画面を呼び出していますが、その際にユーザ名、IDトークン、サインアウト用関数を渡しています。実際の本番では React Router を使って画面遷移を制御しています。
しかし、「ログイン直後に限って」どうしてもIDトークンを取得できない現象があり、苦肉の策で typeof でIDトークンを参照しエラーを発生させ、IDトークンが取得できなかったときには画面をリロードする、という処理を仕込んでログイン失敗エラーを回避しています。リロード後は、当然ログイン済みであるためログイン後画面に誘導されます。
IDトークンをアプリ内の処理で使用しない場合はこの処理は不要です。私は API Gateway と連携する際に Cognito Authorizer を使用しているのでIDトークンが必要でした。
import React from 'react'; import { useAuthenticator } from '@aws-amplify/ui-react'; import Home from './pages/Home'; //ログイン後の画面 const Loggedin = () => { const { user, signOut } = useAuthenticator((context) => [context.user]); //ログイン直後のみidTokenを受け取れずエラーになってしまうため、画面リロードを実行 try { console.log(typeof user.signInUserSession.idToken.jwtToken); } catch (e) { window.location.reload(); } return ( <div id="page-wrapper"> <Home username={user.username} idToken={user.signInUserSession.idToken.jwtToken} signOut={signOut} /> </div> ); }; export default Loggedin;
サインアウト用関数は、公式ドキュメント通り <button> タグ等で onClick={signOut} のように記述するとサインアウトボタンとして機能します。関数だけが提供されるようになったので、任意のデザインのボタンを使用できるようになりました。Amplify UI v1 ではサインアウトボタンUIとして提供されており、デザインを変えることができませんでした。
まとめ
いかがでしたでしょうか?
Amplify UI を使用すると簡単にログイン画面が作れるのですが、公式ドキュメントに記載されている内容だけでは意味がわからなかったり、そのままでは動かない例もあったので、動くサンプルを紹介いたしました。
v1 から v2 への移行も、v1 で導入された方であればそんなに苦労せずにできると思います。
こちらの内容以外の記述でももちろん動きますので、あくまでも一例として、みなさまのお役に立てば幸いです。