Add authentication
The next feature you will be adding is authentication.
Authentication with Amplify
Amplify uses Amazon Cognito as its authentication provider. Amazon Cognito is a robust user directory service that handles user registration, authentication, account recovery & other operations. In this tutorial, you'll learn how to add authentication to your application using Amazon Cognito and username/password login.
Create authentication service
To add authentication to your app, run this command:
1amplify add auth
Select the defaults for the following prompts:
1? Do you want to use the default authentication and security configuration? Default configuration2
3Warning: you will not be able to edit these selections.4? How do you want users to be able to sign in? Username5? Do you want to configure advanced settings? No, I am done.
To deploy the service, run the push
command:
1amplify push2
3✔ Are you sure you want to continue? (Y/n) · yes
Now, the authentication service has been deployed and you can start using it. To view the deployed services in your project at any time, go to Amplify Console by running the following command:
1amplify console
Create login UI
Now that you have your authentication service deployed to AWS, it's time to add authentication to your app. Creating a login flow can be quite difficult and time consuming to get right. Luckily, Amplify UI has an Authenticator
component that provides an entire authentication flow for you, using the configuration you specified in amplifyconfiguration.json.
Install Amplify UI
The @aws-amplify/ui-react-native
package includes React Native specific UI components you'll use to build your app. Install it with the following command:
1expo install @aws-amplify/ui-react-native react-native-safe-area-context
1npm install @aws-amplify/ui-react-native react-native-safe-area-context
You will also need to install the pod dependencies for iOS:
1npx pod-install
After installing pod dependencies, rebuild the app:
1npm run ios
Add the Amplify UI Authenticator component
Open App.js and make the following changes:
Open App.tsx and make the following changes:
- Import the
withAuthenticator
Higher-Order Component anduseAuthenticator
hook:
1import {2 withAuthenticator,3 useAuthenticator4} from '@aws-amplify/ui-react-native';
- Create a custom
SignOutButton
component that will be used within the App component and only re-render when itsuser
context changes. Destructure theuser
andsignOut
properties returned from the hook.
1// retrieves only the current value of 'user' from 'useAuthenticator'2const userSelector = (context) => [context.user];3
4const SignOutButton = () => {5 const { user, signOut } = useAuthenticator(userSelector);6 return (7 <Pressable onPress={signOut} style={styles.buttonContainer}>8 <Text style={styles.buttonText}>9 Hello, {user.username}! Click here to sign out!10 </Text>11 </Pressable>12 );13};
- Add the
SignOutButton
component to yourApp
component inside of the containers you defined in the last section:
1// ...2return (3 <SafeAreaView style={styles.container}>4 <View style={styles.container}>5 <SignOutButton />6 </View>7 </SafeAreaView>8);9//...
- Lastly, wrap your
App
export with thewithAuthenticator
Amplify UI component:
1export default withAuthenticator(App);
Run the app to see the new authentication flow protecting the app:
1npm start
Now you should see the app load with an authentication flow allowing users to sign up and sign in. Below you can find a full sample of the code:
1import React, { useEffect, useState } from 'react';2import {3 StyleSheet,4 Text,5 View,6 TextInput,7 Pressable,8 SafeAreaView9} from 'react-native';10import { generateClient } from 'aws-amplify/api';11import { createTodo } from './src/graphql/mutations';12import { listTodos } from './src/graphql/queries';13import {14 withAuthenticator,15 useAuthenticator16} from '@aws-amplify/ui-react-native';17
18import { Amplify } from 'aws-amplify';19import config from './src/amplifyconfiguration.json';20Amplify.configure(config);21
22// retrieves only the current value of 'user' from 'useAuthenticator'23const userSelector = (context) => [context.user];24
25const SignOutButton = () => {26 const { user, signOut } = useAuthenticator(userSelector);27 return (28 <Pressable onPress={signOut} style={styles.buttonContainer}>29 <Text style={styles.buttonText}>30 Hello, {user.username}! Click here to sign out!31 </Text>32 </Pressable>33 );34};35
36const initialFormState = { name: '', description: '' };37const client = generateClient();38
39const App = () => {40 const [formState, setFormState] = useState(initialFormState);41 const [todos, setTodos] = useState([]);42
43 useEffect(() => {44 fetchTodos();45 }, []);46
47 function setInput(key, value) {48 setFormState({ ...formState, [key]: value });49 }50
51 async function fetchTodos() {52 try {53 const todoData = await client.graphql({54 query: listTodos55 });56 const todos = todoData.data.listTodos.items;57 setTodos(todos);58 } catch (err) {59 console.log('error fetching todos');60 }61 }62
63 async function addTodo() {64 try {65 if (!formState.name || !formState.description) return;66 const todo = { ...formState };67 setTodos([...todos, todo]);68 setFormState(initialFormState);69 await client.graphql({70 query: createTodo,71 variables: { input: todo }72 });73 } catch (err) {74 console.log('error creating todo:', err);75 }76 }77
78 return (79 <SafeAreaView style={styles.container}>80 <View style={styles.container}>81 <SignOutButton />82 <TextInput83 onChangeText={(value) => setInput('name', value)}84 style={styles.input}85 value={formState.name}86 placeholder="Name"87 />88 <TextInput89 onChangeText={(value) => setInput('description', value)}90 style={styles.input}91 value={formState.description}92 placeholder="Description"93 />94 <Pressable onPress={addTodo} style={styles.buttonContainer}>95 <Text style={styles.buttonText}>Create todo</Text>96 </Pressable>97 {todos.map((todo, index) => (98 <View key={todo.id ? todo.id : index} style={styles.todo}>99 <Text style={styles.todoName}>{todo.name}</Text>100 <Text style={styles.todoDescription}>{todo.description}</Text>101 </View>102 ))}103 </View>104 </SafeAreaView>105 );106};107
108export default withAuthenticator(App);109
110const styles = StyleSheet.create({111 container: { width: 400, flex: 1, padding: 20, alignSelf: 'center' },112 todo: { marginBottom: 15 },113 input: {114 backgroundColor: '#ddd',115 marginBottom: 10,116 padding: 8,117 fontSize: 18118 },119 todoName: { fontSize: 20, fontWeight: 'bold' },120 buttonContainer: {121 alignSelf: 'center',122 backgroundColor: 'black',123 paddingHorizontal: 8124 },125 buttonText: { color: 'white', padding: 16, fontSize: 18 }126});
1import React, { useEffect, useState } from 'react';2import {3 StyleSheet,4 Text,5 View,6 TextInput,7 Pressable,8 SafeAreaView9} from 'react-native';10import { generateClient } from 'aws-amplify/api';11import { createTodo } from './src/graphql/mutations';12import { listTodos } from './src/graphql/queries';13import {14 withAuthenticator,15 useAuthenticator16} from '@aws-amplify/ui-react-native';17
18// retrieves only the current value of 'user' from 'useAuthenticator'19const userSelector = (context) => [context.user];20
21const SignOutButton = () => {22 const { user, signOut } = useAuthenticator(userSelector);23 return (24 <Pressable onPress={signOut} style={styles.buttonContainer}>25 <Text style={styles.buttonText}>26 Hello, {user.username}! Click here to sign out!27 </Text>28 </Pressable>29 );30};31
32const initialFormState = { name: '', description: '' };33const client = generateClient();34
35const App = () => {36 const [formState, setFormState] = useState(initialFormState);37 const [todos, setTodos] = useState([]);38
39 useEffect(() => {40 fetchTodos();41 }, []);42
43 function setInput(key, value) {44 setFormState({ ...formState, [key]: value });45 }46
47 async function fetchTodos() {48 try {49 const todoData = await client.graphql({50 query: listTodos51 });52 const todos = todoData.data.listTodos.items;53 setTodos(todos);54 } catch (err) {55 console.log('error fetching todos');56 }57 }58
59 async function addTodo() {60 try {61 if (!formState.name || !formState.description) return;62 const todo = { ...formState };63 setTodos([...todos, todo]);64 setFormState(initialFormState);65 await client.graphql({66 query: createTodo,67 variables: { input: todo }68 });69 } catch (err) {70 console.log('error creating todo:', err);71 }72 }73
74 return (75 <SafeAreaView style={styles.container}>76 <View style={styles.container}>77 <SignOutButton />78 <TextInput79 onChangeText={(value) => setInput('name', value)}80 style={styles.input}81 value={formState.name}82 placeholder="Name"83 />84 <TextInput85 onChangeText={(value) => setInput('description', value)}86 style={styles.input}87 value={formState.description}88 placeholder="Description"89 />90 <Pressable onPress={addTodo} style={styles.buttonContainer}>91 <Text style={styles.buttonText}>Create todo</Text>92 </Pressable>93 {todos.map((todo, index) => (94 <View key={todo.id ? todo.id : index} style={styles.todo}>95 <Text style={styles.todoName}>{todo.name}</Text>96 <Text style={styles.todoDescription}>{todo.description}</Text>97 </View>98 ))}99 </View>100 </SafeAreaView>101 );102};103
104export default withAuthenticator(App);105
106const styles = StyleSheet.create({107 container: { width: 400, flex: 1, padding: 20, alignSelf: 'center' },108 todo: { marginBottom: 15 },109 input: {110 backgroundColor: '#ddd',111 marginBottom: 10,112 padding: 8,113 fontSize: 18114 },115 todoName: { fontSize: 20, fontWeight: 'bold' },116 buttonContainer: {117 alignSelf: 'center',118 backgroundColor: 'black',119 paddingHorizontal: 8120 },121 buttonText: { color: 'white', padding: 16, fontSize: 18 }122});
Using Amplify UI connected components makes it easier to manage styling across your entire app.
In this example, you used the Amplify UI library and the withAuthenticator
Higher-Order Component to quickly get up and running with a real-world authentication flow. You can also customize this component to add or remove fields, update styling, or other configurations. You can even override function calls if needed. To learn more, visit the Amplify UI documentation website.
In addition to withAuthenticator
, you can build custom authentication flows with the Amplify Library for JS. Amplify's Auth package has several methods including signUp
, signIn
, forgotPassword
, and signOut
that allow you full control over all aspects of the user authentication flow.