認証を追加
次に追加する機能は認証です。
Amplifyを使用した認証
Amplifyは認証プロバイダーとしてAmazon Cognitoを使用します。Amazon Cognitoは堅牢なユーザーディレクトリサービスで、ユーザー登録、認証、アカウント復旧などの操作を処理します。このチュートリアルでは、Amazon Cognitoとユーザー名/パスワードログインを使用してアプリケーションに認証を追加する方法を学習します。
認証サービスを作成
アプリに認証を追加するには、以下のコマンドを実行します:
amplify add auth次のプロンプトのデフォルトを選択します:
? Do you want to use the default authentication and security configuration? Default configuration
Warning: you will not be able to edit these selections.? How do you want users to be able to sign in? Username? Do you want to configure advanced settings? No, I am done.サービスをデプロイするには、pushコマンドを実行します:
amplify push
✔ Are you sure you want to continue? (Y/n) · yesこれで認証サービスがデプロイされ、使用を開始できます。いつでもプロジェクトにデプロイされたサービスを表示するには、以下のコマンドを実行してAmplify Consoleにアクセスします:
amplify consoleログインUIを作成
AWS に認証サービスがデプロイされたので、アプリに認証を追加する時間です。ログインフローの作成は非常に難しく、時間がかかる場合があります。幸い、Amplify UIにはAuthenticatorコンポーネントがあり、amplifyconfiguration.jsonで指定した設定を使用して、認証フロー全体を提供します。
Amplify UIをインストール
@aws-amplify/ui-reactパッケージには、アプリを構築するために使用するReact固有のUIコンポーネントが含まれています。次のコマンドでインストールします:
npm install @aws-amplify/ui-reactAmplify UI Authenticatorコンポーネントを追加
src/App.tsxまたはsrc/App.jsxを開いて、以下の変更を加えます:
withAuthenticatorコンポーネントをインポートします:
import { withAuthenticator, Button, Heading } from '@aws-amplify/ui-react';import '@aws-amplify/ui-react/styles.css';{signOut, user}をAppコンポーネントに渡します(TypeScriptの場合は、signOutとuserプロップのタイプも追加する必要があります):
import { type AuthUser } from "aws-amplify/auth";import { type UseAuthenticator } from "@aws-amplify/ui-react-core";
...
type AppProps = { signOut?: UseAuthenticator["signOut"]; //() => void; user?: AuthUser;};
...
const App: React.FC<AppProps> = ({ signOut, user }) => { // ...};const App = ({ signOut, user }) => { // ...};Appコンポーネントの上部にこのヘッディングとボタンブロックを追加します:
return ( <div style={styles.container}> <Heading level={1}>Hello {user.username}</Heading> <Button onClick={signOut}>Sign out</Button> <h2>Amplify Todos</h2> ... </div>);- 最後に、
AppエクスポートをAmplify UIのwithAuthenticatorコンポーネントでラップします:
export default withAuthenticator(App);アプリを実行して、アプリを保護する新しい認証フローを確認します:
npm run devこれで、アプリがユーザーがサインアップおよびサインインできる認証フローと共に読み込まれることがわかります。
完全なコード例をレビュー
以下のコードをすべて示します:
import { useEffect, useState } from "react";
import { generateClient } from "aws-amplify/api";
import { createTodo } from "./graphql/mutations";import { listTodos } from "./graphql/queries";import { type CreateTodoInput, type Todo } from "./API";
import { withAuthenticator, Button, Heading } from "@aws-amplify/ui-react";import { type AuthUser } from "aws-amplify/auth";import { type UseAuthenticator } from "@aws-amplify/ui-react-core";import "@aws-amplify/ui-react/styles.css";
const initialState: CreateTodoInput = { name: "", description: "" };const client = generateClient();
type AppProps = { signOut?: UseAuthenticator["signOut"]; //() => void; user?: AuthUser;};
const App: React.FC<AppProps> = ({ signOut, user }) => { const [formState, setFormState] = useState<CreateTodoInput>(initialState); const [todos, setTodos] = useState<Todo[] | CreateTodoInput[]>([]);
useEffect(() => { fetchTodos(); }, []);
async function fetchTodos() { try { const todoData = await client.graphql({ query: listTodos, }); const todos = todoData.data.listTodos.items; setTodos(todos); } catch (err) { console.log("error fetching todos"); } }
async function addTodo() { try { if (!formState.name || !formState.description) return; const todo = { ...formState }; setTodos([...todos, todo]); setFormState(initialState); await client.graphql({ query: createTodo, variables: { input: todo, }, }); } catch (err) { console.log("error creating todo:", err); } }
return ( <div style={styles.container}> <Heading level={1}>Hello {user?.username}</Heading> <Button onClick={signOut}>Sign out</Button> <h2>Amplify Todos</h2> <input onChange={(event) => setFormState({ ...formState, name: event.target.value }) } style={styles.input} value={formState.name} placeholder="Name" /> <input onChange={(event) => setFormState({ ...formState, description: event.target.value }) } style={styles.input} value={formState.description as string} placeholder="Description" /> <button style={styles.button} onClick={addTodo}> Create Todo </button> {todos.map((todo, index) => ( <div key={todo.id ? todo.id : index} style={styles.todo}> <p style={styles.todoName}>{todo.name}</p> <p style={styles.todoDescription}>{todo.description}</p> </div> ))} </div> );};
const styles = { container: { width: 400, margin: "0 auto", display: "flex", flexDirection: "column", justifyContent: "center", padding: 20, }, todo: { marginBottom: 15 }, input: { border: "none", backgroundColor: "#ddd", marginBottom: 10, padding: 8, fontSize: 18, }, todoName: { fontSize: 20, fontWeight: "bold" }, todoDescription: { marginBottom: 0 }, button: { backgroundColor: "black", color: "white", outline: "none", fontSize: 18, padding: "12px 0px", },} as const;
export default withAuthenticator(App);import { useEffect, useState } from 'react';
import { generateClient } from 'aws-amplify/api';
import { createTodo } from './graphql/mutations';import { listTodos } from './graphql/queries';
import { withAuthenticator, Button, Heading } from '@aws-amplify/ui-react';import '@aws-amplify/ui-react/styles.css';
const initialState = { name: '', description: '' };const client = generateClient();
const App = ({ signOut, user }) => { const [formState, setFormState] = useState(initialState); const [todos, setTodos] = useState([]);
useEffect(() => { fetchTodos(); }, []);
function setInput(key, value) { setFormState({ ...formState, [key]: value }); }
async function fetchTodos() { try { const todoData = await client.graphql({ query: listTodos }); const todos = todoData.data.listTodos.items; setTodos(todos); } catch (err) { console.log('error fetching todos'); } }
async function addTodo() { try { if (!formState.name || !formState.description) return; const todo = { ...formState }; setTodos([...todos, todo]); setFormState(initialState); await client.graphql({ query: createTodo, variables: { input: todo } }); } catch (err) { console.log('error creating todo:', err); } }
return ( <div style={styles.container}> <Heading level={1}>Hello {user.username}</Heading> <Button onClick={signOut} style={styles.button}> Sign out </Button> <h2>Amplify Todos</h2> <input onChange={(event) => setInput('name', event.target.value)} style={styles.input} value={formState.name} placeholder="Name" /> <input onChange={(event) => setInput('description', event.target.value)} style={styles.input} value={formState.description} placeholder="Description" /> <button style={styles.button} onClick={addTodo}> Create Todo </button> {todos.map((todo, index) => ( <div key={todo.id ? todo.id : index} style={styles.todo}> <p style={styles.todoName}>{todo.name}</p> <p style={styles.todoDescription}>{todo.description}</p> </div> ))} </div> );};
const styles = { container: { width: 400, margin: '0 auto', display: 'flex', flexDirection: 'column', justifyContent: 'center', padding: 20 }, todo: { marginBottom: 15 }, input: { border: 'none', backgroundColor: '#ddd', marginBottom: 10, padding: 8, fontSize: 18 }, todoName: { fontSize: 20, fontWeight: 'bold' }, todoDescription: { marginBottom: 0 }, button: { backgroundColor: 'black', color: 'white', outline: 'none', fontSize: 18, padding: '12px 0px' }};
export default withAuthenticator(App);ボーナス: Amplify UIプリミティブを使用
2つのAmplify UIコンポーネントHeadingとButtonを使用しました。pタグをTextに、inputをTextFieldに、divをViewに置き換えることで、アプリの残りをAmplify UIコンポーネントに変換することもできます。
- Amplify UIからインポートされたコンポーネントに
Text、TextField、Viewコンポーネントを追加します:
import { withAuthenticator, Button, Heading, Text, TextField, View} from '@aws-amplify/ui-react';AppコンポーネントのpタグをTextに、inputをTextFieldに、divをViewに置き換えます:
<View style={styles.container}> <Heading level={1}>Hello {user.username}</Heading> <Button style={styles.button} onClick={signOut}> Sign out </Button> <Heading level={2}>Amplify Todos</Heading> <TextField placeholder="Name" onChange={(event) => setInput('name', event.target.value)} style={styles.input} defaultValue={formState.name} /> <TextField placeholder="Description" onChange={(event) => setInput('description', event.target.value)} style={styles.input} defaultValue={formState.description} /> <Button style={styles.button} onClick={addTodo}> Create Todo </Button> {todos.map((todo, index) => ( <View key={todo.id ? todo.id : index} style={styles.todo}> <Text style={styles.todoName}>{todo.name}</Text> <Text style={styles.todoDescription}>{todo.description}</Text> </View> ))}</View>Amplify UIの接続されたコンポーネントを使用すると、アプリ全体のスタイルを管理しやすくなります。
この例では、Amplify UIライブラリとwithAuthenticator高次コンポーネントを使用して、実世界の認証フローすぐに開始しました。このコンポーネントをカスタマイズして、フィールドを追加または削除したり、スタイルを更新したり、その他の設定を行ったりすることもできます。必要に応じて関数呼び出しをオーバーライドすることもできます。詳細については、Amplify UIドキュメントウェブサイトにアクセスしてください。
withAuthenticatorに加えて、Amplify Library for JSでカスタム認証フローを構築することもできます。AmplifyのAuthパッケージにはsignUp、signIn、forgotPassword、signOutなど、ユーザー認証フローのすべての側面を完全に制御できる複数のメソッドがあります。