認証を追加
次に追加する機能は認証です。
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-nativeパッケージには、アプリを構築するために使用するReact Native固有のUIコンポーネントが含まれています。次のコマンドでインストールします:
expo install @aws-amplify/ui-react-native react-native-safe-area-context@^4.2.5npm install @aws-amplify/ui-react-native react-native-safe-area-context@^4.2.5また、iOSのポッド依存関係をインストールする必要があります:
npx pod-installポッド依存関係のインストール後、アプリを再構築します:
npm run iosAmplify UI Authenticatorコンポーネントを追加
App.jsを開いて、以下の変更を加えます:
App.tsxを開いて、以下の変更を加えます:
withAuthenticator高次コンポーネントとuseAuthenticatorフックをインポートします:
import { withAuthenticator, useAuthenticator} from '@aws-amplify/ui-react-native';- Appコンポーネント内で使用するカスタム
SignOutButtonコンポーネントを作成します。このコンポーネントはuserコンテキストが変更された場合のみ再レンダリングされます。フックから返されたuserとsignOutプロパティを分割代入します。
// retrieves only the current value of 'user' from 'useAuthenticator'const userSelector = (context) => [context.user];
const SignOutButton = () => { const { user, signOut } = useAuthenticator(userSelector); return ( <Pressable onPress={signOut} style={styles.buttonContainer}> <Text style={styles.buttonText}> Hello, {user.username}! Click here to sign out! </Text> </Pressable> );};SignOutButtonコンポーネントを前のセクションで定義したコンテナ内のAppコンポーネントに追加します:
// ...return ( <SafeAreaView style={styles.container}> <View style={styles.container}> <SignOutButton /> </View> </SafeAreaView>);//...- 最後に、
AppエクスポートをAmplify UIのwithAuthenticatorコンポーネントでラップします:
export default withAuthenticator(App);アプリを実行して、アプリを保護する新しい認証フローを確認します:
npm startこれで、アプリがユーザーがサインアップおよびサインインできる認証フローと共に読み込まれることがわかります。以下は、コードの完全なサンプルです:
import React, { useEffect, useState } from 'react';import { StyleSheet, Text, View, TextInput, Pressable, SafeAreaView} from 'react-native';import { generateClient } from 'aws-amplify/api';import { createTodo } from './src/graphql/mutations';import { listTodos } from './src/graphql/queries';import { withAuthenticator, useAuthenticator} from '@aws-amplify/ui-react-native';
import { Amplify } from 'aws-amplify';import config from './src/amplifyconfiguration.json';Amplify.configure(config);
// retrieves only the current value of 'user' from 'useAuthenticator'const userSelector = (context) => [context.user];
const SignOutButton = () => { const { user, signOut } = useAuthenticator(userSelector); return ( <Pressable onPress={signOut} style={styles.buttonContainer}> <Text style={styles.buttonText}> Hello, {user.username}! Click here to sign out! </Text> </Pressable> );};
const initialFormState = { name: '', description: '' };const client = generateClient();
const App = () => { const [formState, setFormState] = useState(initialFormState); 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(initialFormState); await client.graphql({ query: createTodo, variables: { input: todo } }); } catch (err) { console.log('error creating todo:', err); } }
return ( <SafeAreaView style={styles.container}> <View style={styles.container}> <SignOutButton /> <TextInput onChangeText={(value) => setInput('name', value)} style={styles.input} value={formState.name} placeholder="Name" /> <TextInput onChangeText={(value) => setInput('description', value)} style={styles.input} value={formState.description} placeholder="Description" /> <Pressable onPress={addTodo} style={styles.buttonContainer}> <Text style={styles.buttonText}>Create todo</Text> </Pressable> {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> </SafeAreaView> );};
export default withAuthenticator(App);
const styles = StyleSheet.create({ container: { width: 400, flex: 1, padding: 20, alignSelf: 'center' }, todo: { marginBottom: 15 }, input: { backgroundColor: '#ddd', marginBottom: 10, padding: 8, fontSize: 18 }, todoName: { fontSize: 20, fontWeight: 'bold' }, buttonContainer: { alignSelf: 'center', backgroundColor: 'black', paddingHorizontal: 8 }, buttonText: { color: 'white', padding: 16, fontSize: 18 }});import React, { useEffect, useState } from 'react';import { StyleSheet, Text, View, TextInput, Pressable, SafeAreaView} from 'react-native';import { generateClient } from 'aws-amplify/api';import { createTodo } from './src/graphql/mutations';import { listTodos } from './src/graphql/queries';import { withAuthenticator, useAuthenticator} from '@aws-amplify/ui-react-native';
// retrieves only the current value of 'user' from 'useAuthenticator'const userSelector = (context) => [context.user];
const SignOutButton = () => { const { user, signOut } = useAuthenticator(userSelector); return ( <Pressable onPress={signOut} style={styles.buttonContainer}> <Text style={styles.buttonText}> Hello, {user.username}! Click here to sign out! </Text> </Pressable> );};
const initialFormState = { name: '', description: '' };const client = generateClient();
const App = () => { const [formState, setFormState] = useState(initialFormState); 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(initialFormState); await client.graphql({ query: createTodo, variables: { input: todo } }); } catch (err) { console.log('error creating todo:', err); } }
return ( <SafeAreaView style={styles.container}> <View style={styles.container}> <SignOutButton /> <TextInput onChangeText={(value) => setInput('name', value)} style={styles.input} value={formState.name} placeholder="Name" /> <TextInput onChangeText={(value) => setInput('description', value)} style={styles.input} value={formState.description} placeholder="Description" /> <Pressable onPress={addTodo} style={styles.buttonContainer}> <Text style={styles.buttonText}>Create todo</Text> </Pressable> {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> </SafeAreaView> );};
export default withAuthenticator(App);
const styles = StyleSheet.create({ container: { width: 400, flex: 1, padding: 20, alignSelf: 'center' }, todo: { marginBottom: 15 }, input: { backgroundColor: '#ddd', marginBottom: 10, padding: 8, fontSize: 18 }, todoName: { fontSize: 20, fontWeight: 'bold' }, buttonContainer: { alignSelf: 'center', backgroundColor: 'black', paddingHorizontal: 8 }, buttonText: { color: 'white', padding: 16, fontSize: 18 }});Amplify UIの接続されたコンポーネントを使用すると、アプリ全体のスタイルを管理しやすくなります。
この例では、Amplify UIライブラリとwithAuthenticator高次コンポーネントを使用して、実世界の認証フローすぐに開始しました。このコンポーネントをカスタマイズして、フィールドを追加または削除したり、スタイルを更新したり、その他の設定を行ったりすることもできます。必要に応じて関数呼び出しをオーバーライドすることもできます。詳細については、Amplify UIドキュメントウェブサイトにアクセスしてください。
withAuthenticatorに加えて、Amplify Library for JSでカスタム認証フローを構築することもできます。AmplifyのAuthパッケージにはsignUp、signIn、forgotPassword、signOutなど、ユーザー認証フローのすべての側面を完全に制御できる複数のメソッドがあります。