AWS cognitoとReactでログインを実装する
こんにちはハトです。業務でcognitoを利用していたのですがかなり躓いたので共有します。
つまづきポイント多すぎ
まずAWSを利用している方でSPAで認証を実現しようとしてネットをあさるといろいろ情報があることに気づく
- cognitoの利用
- Amplify の auth の利用
わけわかめとなる。次にAmplifyを調べる
- cliがあるよ
- cliでコマンド打てばGraphQLとか使えるよ
- Amplify Consoleあるよ。静的ホスティングが自動化できるよ
ん?認証だけ使いたいのになんでGraphQLいれるの?みたいになる。
とりあえず、僕がつまずいたポイントや知見を共有しようと思います。
前提
すでにcreate-react-appなどで、何かしらのreactプロジェクトを作ってある。
AWSでconigtoを使うだけならamplifyはいらないけど、amplifyライブラリは便利だよ
厳密にはamplifyのcliは必要ないけどamplifyライブラリは便利です。
まずは次の点を理解してください。
- amplifyはいろいろなサービスを複合したものである。それぞれをカテゴリという。
- カテゴリにはauthみたいな名前付いてるけど、結局中身はcognitoとかS3とか他のサービス。
- cliコマンドを利用して各カテゴリの環境構築を簡単に行えることが売り。だからかドキュメントにはかならずcliコマンドから始める風に書いてある。これが初心者を戸惑わせる。
- amplifyのcliとreactなどで実際に使うamplifyライブラリは別物。
- amplifyのcliコマンドを使って環境構築しなくてもamplifyライブラリは使える。
- amplifyライブラリはマジ便利
amplifyのドキュメントはとりあえずcliから始めてますが、それだとamplifyの理解とcliの理解が必要になります。一気に理解するのはしんどいため、とりあえずreactでなんか流行ってるamplify使って認証を実現したいかたはcliをスキップするとよいでしょう。後のセクションではそれを行います。
amplifyは既存のcognitoは使えないよ
cognitoは先に作っていました。そのあとamplifyのcliを使いamplify add auth
をしたら、完全に新しいcognitoが作られており、どうやら既存のcognitoは使えないとのことでした。既存のcognitoを使いたい場合は、別途アプリケーションから設定する必要があります。
import Amplify from 'aws-amplify';
// 既存のcognitoの設定
Amplify.configure({
aws_project_region: process.env.REACT_APP_AWS_PROJECT_REGION,
aws_cognito_identity_pool_id:
process.env.REACT_APP_AWS_COGNITO_IDENTITY_POOL_ID,
aws_cognito_region: process.env.REACT_APP_AWS_COGNITO_REGION,
aws_user_pools_id: process.env.REACT_APP_AWS_USER_POOLS_ID,
aws_user_pools_web_client_id: process.env.REACT_APP_AWS_USER_POOLS_CLIENT_ID,
});
!!追記!!
調べてみたらようやく既存のcognitoを使えるようになっていました。
Reactにamplifyライブラリをinstallする
amplifyのcliを使わずに、amplifyの認証ライブラリを利用しようと思います。
まずは、既存のreactアプリにamplifyのライブラリをinstallします。
npm install aws-amplify
つぎにApp.tsxで設定を書けば使えるようになります。
App.tsxもしくはApp.jsx
import Amplify from 'aws-amplify';
import { cognitoConstants } from 'constants/auth';
Amplify.configure(cognitoConstants);
環境変数にあらかじめ設定値をいれておく。identity_poolなどは使わなければ空でよい。
constants/auth
export const cognitoConstants = {
aws_project_region: process.env.REACT_APP_AWS_PROJECT_REGION,
aws_cognito_identity_pool_id:
process.env.REACT_APP_AWS_COGNITO_IDENTITY_POOL_ID,
aws_cognito_region: process.env.REACT_APP_AWS_COGNITO_REGION,
aws_user_pools_id: process.env.REACT_APP_AWS_USER_POOLS_ID,
aws_user_pools_web_client_id: process.env.REACT_APP_AWS_USER_POOLS_CLIENT_ID,
};
おそらくドキュメントは次のようになっているのではないでしょうか?
import Amplify from 'aws-amplify';
import awsconfig from './aws-exports'; // ここが違う
Amplify.configure(awsconfig);
./aws-exports
というファイルはamplify cliコマンドを実行したときに吐き出されるファイルです。amplify cliで生成された環境の設定値がのっています。今回はcliを使わない想定で作ったのでここを自分で設定しました。
次はUIとログインapiの実装部分ですが、ここで2つの道があります。
- 提供されているUIライブラリを使う
- UIは自身で作って、裏側のcognitoとの通信だけamplifyのauthライブラリに委託する。
1.提供されているUIライブラリを使う
1はめちゃくちゃ簡単です。
aws-amplifyのほかにUIライブラリをinstallします。
npm install @aws-amplify/ui-react
ちなみに、ややこしいことにaws-amplify-react
と@aws-amplify/ui-react
2つのライブラリがありますが、@aws-amplify/ui-react
のほうが新しい方です。ネットには2つのライブラリのコードが混じって記事になっていますので、ご注意ください。
公式サイトをちゃんと参考にしてね。
App.tsxにて次のように設定します。やり方は2つあります。
つぎのようにwithAuthenticator
hocを利用するか。
import React from 'react';
import { withAuthenticator, AmplifySignOut } from '@aws-amplify/ui-react';
import Amplify from 'aws-amplify';
import { cognitoConstants } from 'constants/auth';
Amplify.configure(cognitoConstants);
const App = () => (
<div>
<AmplifySignOut />
My App
</div>
);
export default withAuthenticator(App);
AmplifyAuthenticator
で挟むかです。
import React from "react";
import Amplify from "aws-amplify";
import {AmplifyAuthenticator, AmplifySignOut} from "@aws-amplify/ui-react";
import Amplify from 'aws-amplify';
import { cognitoConstants } from 'constants/auth';
Amplify.configure(cognitoConstants);
const App = () => (
<AmplifyAuthenticator>
<div>
My App
<AmplifySignOut />
</div>
</AmplifyAuthenticator>
);
違いは、設定できるプロパティがAmplifyAuthenticator
のほうが豊富なことです。すぐデフォルトで使いたい方はwithAuthenticator
を使えば良いと思います。ただ公式ドキュメント的にはAmplifyAuthenticator
をよく使っているみたいですね。
公式推奨の方法。
ログインしているときは、通常のレイアウト、ログインしていないときはAmplifyAuthenticator
のレイアウトにする。
import React from 'react';
import './App.css';
import Amplify from 'aws-amplify';
import { AmplifyAuthenticator, AmplifySignOut } from '@aws-amplify/ui-react';
import { AuthState, onAuthUIStateChange } from '@aws-amplify/ui-components';
import { cognitoConstants } from 'constants/auth';
Amplify.configure(cognitoConstants);
const AuthStateApp: React.FunctionComponent = () => {
const [authState, setAuthState] = React.useState<AuthState>();
const [user, setUser] = React.useState<object | undefined>();
React.useEffect(() => {
return onAuthUIStateChange((nextAuthState, authData) => {
setAuthState(nextAuthState);
setUser(authData)
});
}, []);
return authState === AuthState.SignedIn && user ? (
<div className="App">
<div>Hello, {user.username}</div>
<AmplifySignOut />
</div>
) : (
<AmplifyAuthenticator />
);
}
export default AuthStateApp;
これだけでデフォルトのUIとともにログイン機構ができます。すごいですね。
提供されているUIテーマの色を変更する
簡単です。グローバルcssに追加します。
index.css
:root {
--amplify-primary-color: #ff6347;
--amplify-primary-tint: #ff7359;
--amplify-primary-shade: #e0573e;
}
amplify-sign-in {
--footer-color: cyan;
}
どのコンポーネントがどのcss変数名に対応するのかは、公式サイトを御覧ください。
コンポーネントのカスタマイズ(例えば、サインアップフォームの設定など)
明示的にカスタマイズしたいコンポーネントをAmplifyAuthenticator
に入れてください。その際にslot
に対応する文字列をいれる必要があります。
import React from 'react';
import './App.css';
import Amplify from 'aws-amplify';
import { AmplifyAuthenticator, AmplifySignUp, AmplifySignOut } from '@aws-amplify/ui-react';
import { AuthState, onAuthUIStateChange } from '@aws-amplify/ui-components';
import { cognitoConstants } from 'constants/auth';
Amplify.configure(cognitoConstants);
const AuthStateApp = () => {
const [authState, setAuthState] = React.useState();
const [user, setUser] = React.useState();
React.useEffect(() => {
return onAuthUIStateChange((nextAuthState, authData) => {
setAuthState(nextAuthState);
setUser(authData)
});
}, []);
return authState === AuthState.SignedIn && user ? (
<div className="App">
<div>Hello, {user.username}</div>
<AmplifySignOut />
</div>
) : (
<AmplifyAuthenticator>
<AmplifySignUp
slot="sign-up"
formFields={[
{ type: "username" },
{ type: "password" },
{ type: "email" }
]}
/>
</AmplifyAuthenticator>
);
}
export default AuthStateApp;
2.UIは自身で作って、裏側のcognitoとの通信だけamplifyのauthライブラリに委託する
UIライブラリ@aws-amplify/ui-react
をinstallしない方法です。amplify Authライブラリでゴリゴリに書きます。
amplify-authのスターターキットのコードが参考になると思います(丸投げ)
もしくはこちらです。
こちらのよいところは、routingがやりやすいことですね。あとはログインなどに伴って独自の処理をもたせたいときなどです。
!!追記!!
Authライブラリの使い方を記事にしました!
ログイン状態をreduxで管理する
茨の道でした。興味ある方がいればもう少し書こうかなと思います。
ちなみにログイン状態をreduxで管理しなくても、Authライブラリを使えば現セッションのユーザー情報やいまユーザーがログインしているかどうかも調べることができます。
また、amplifyのHubライブラリを使えばpub/subができreduxいらないです。
僕自身は、最初にreduxを導入していたのと、コードのあちこちでamplify特有(ベンダー固有)のAuthが散らばるのが嫌で、reduxでログイン状態を管理することにしました。
茨の道でした。
日本語化する
エラー文を日本語化したいと思いこちらのサイトをまんま参考にしました。
UIの日本語化ふくめるのであればこちらのサイトのほうが参考になるかも。
App.tsx
import Amplify, { I18n } from 'aws-amplify';
import { cognitoConstants } from 'constants/auth'; // これさっきと一緒
import { COGNITO_ERROR } from 'constants/i18n';
// cognito エラーの日本語化
I18n.putVocabularies(COGNITO_ERROR);
I18n.setLanguage('ja');
Amplify.configure(cognitoConstants);
constants/i18n
export const COGNITO_ERROR = {
ja: {
'User does not exist.': 'ユーザーが存在しません',
'Incorrect username or password.': 'ユーザー名またはパスワードが違います',
'User is not confirmed.': 'ユーザーは検証されていません',
'User already exists': 'ユーザーは既に存在します',
'Invalid verification code provided, please try again.':
'指定された確認コードが無効です。もう一度お試しください',
'Invalid password format': 'パスワードのフォーマットが不正です',
'Account recovery requires verified contact information':
'アカウントの復元には確認済みの連絡先情報が必要です',
'Invalid phone number format':
'不正な電話番号フォーマットです。 電話番号は次のフォーマットで入力してください: +12345678900',
'An account with the given email already exists.':
'そのメールアドレスは既に存在します',
'Username cannot be empty': 'ユーザー名は必須です',
'Password attempts exceeded': 'パスワード試行回数が超過しました',
'Attempt limit exceeded, please try after some time.':
'試行制限を超過しました。しばらくしてからもう一度お試しください',
'Username/client id combination not found.': 'ユーザーが存在しません',
'CUSTOM_AUTH is not enabled for the client.': 'パスワードは必須です', // 本来の意味とは異なるが、パスワード未入力時に発生するのでこの訳としている
'Password does not conform to policy: Password not long enough':
'パスワードは8文字以上を入力してください (8文字以上の大文字小文字を含む英数字)', // 適宜修正
'Password does not conform to policy: Password must have uppercase characters':
'パスワードには大文字を含めてください (8文字以上の大文字小文字を含む英数字)', // 適宜修正
'Password does not conform to policy: Password must have lowercase characters':
'パスワードには小文字を含めてください (8文字以上の大文字小文字を含む英数字)', // 適宜修正
'Password does not conform to policy: Password must have numeric characters':
'パスワードには数字を含めてください (8文字以上の大文字小文字を含む英数字)', // 適宜修正
"1 validation error detected: Value at 'password' failed to satisfy constraint: Member must have length greater than or equal to 6":
'パスワードは8文字以上、大文字小文字を含む英数字を指定してください', // 適宜修正。本来の意味とは異なるがこれで明示している。
"2 validation errors detected: Value at 'password' failed to satisfy constraint: Member must have length greater than or equal to 6; Value at 'password' failed to satisfy constraint: Member must satisfy regular expression pattern: ^[S]+.*[S]+$":
'パスワードは8文字以上、大文字小文字を含む英数字を指定してください', // 適宜修正。本来の意味とは異なるがこれで明示している。
},
};
cognitoつまづきポイント
基本的に設定は変更できないから、本番に適用する前に十分に検証すること
本番に適用してしまったあとに設定を変更する必要がある場合の救済措置はある。それはcognitoのmigrationトリガーを利用すること。このトリガーを有効にすると、もしログイン時にuserpoolにusernameが存在しなければこのトリガーが発動される。トリガー先にはlambdaを設定する。lambdaにusernameとpasswordが渡されるので、既存のuserpoolにアクセスして、ユーザー情報を返して上げると、cognitoが自動的にユーザーを登録してくれる。
lambdaをどう書くかなどは、神記事のこちらが詳しい
注意点として、移行期間中はauthenticationFlowTypeはあまりセキュアでないUSER_PASSWORD_AUTH
にする必要がある。
export const cognitoConstants = {
aws_project_region: process.env.REACT_APP_AWS_PROJECT_REGION,
aws_cognito_identity_pool_id:
process.env.REACT_APP_AWS_COGNITO_IDENTITY_POOL_ID,
aws_cognito_region: process.env.REACT_APP_AWS_COGNITO_REGION,
aws_user_pools_id: process.env.REACT_APP_AWS_USER_POOLS_ID,
aws_user_pools_web_client_id: process.env.REACT_APP_AWS_USER_POOLS_CLIENT_ID,
authenticationFlowType: process.env.REACT_APP_AWS_COGNITO_FLOW_TYPE, // USER_PASSWORD_AUTHにする
};
usernameには基本的にメールなどでaliasできるように設定する
usernameのエイリアス設定忘れると、usernameにemailが設定される。usernameは基本的に変更不可なので、メールアドレスが変更できず詰むことに。
注意: ここもしメールアドレスを変更できる方法をしっているかたがいらっしゃいましたら教えて下さい。
メアド変更するとログインできなくなる可能性がある
こちらで詳しく解説しています。
amplify Authライブラリについて
amazon-cognito-identity-jsのラップライブラリですね。
ややこしいことに似たメソッドが多くあります。かんたんな認識としてadminという名前がついているメソッドは基本的にサーバサイドで使うことが多いとだけ覚えておくとよいかと思います。
時間があればここをもう少し充実させます。
-> 充実させました!
手動でカスタマイズしたい猛者へ参考サイト
スターターキット。多分これを参考にするのが正解なきがする。
普通に導入したやつ
reduxとの統合
Discussion