Page updated Nov 15, 2023

Preview: AWS Amplify's new code-first DX (Gen 2)

The next generation of Amplify's backend building experience with a TypeScript-first DX.

Get started

Add authentication

The next feature you will be adding is authentication. Amplify uses Amazon Cognito as the main 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:

amplify add auth
1amplify add auth

Select the defaults for the following prompts:

? 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.
1? Do you want to use the default authentication and security configuration? Default configuration
2Warning: you will not be able to edit these selections.
3? How do you want users to be able to sign in? Username
4? Do you want to configure advanced settings? No, I am done.

To deploy the service, run the push command:

amplify push
1amplify push

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:

amplify console
1amplify console

Create login UI

Creating a login flow can be quite difficult and time consuming to get right. Luckily, Amplify UI has an authentication 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 package includes React specific UI components you'll use to build your app. Install it with this:

npm install @aws-amplify/ui-react
1npm install @aws-amplify/ui-react

Add the Amplify UI Authenticator component

Open src/App.jsx and make the following changes:

  1. Import the withAuthenticator component:
import { withAuthenticator, Button, Heading } from '@aws-amplify/ui-react'; import '@aws-amplify/ui-react/styles.css';
1import { withAuthenticator, Button, Heading } from '@aws-amplify/ui-react';
2import '@aws-amplify/ui-react/styles.css';
  1. Pass {signOut, user} to the App component. (For Vite's default React configuration, you'll also need to add "PropTypes" to validate the signOut and user props):
/* src/App.jsx */ import PropTypes from 'prop-types' // ... other imports const App = ({ signOut, user }) => { // ... } App.propTypes = { signOut: PropTypes.func, user: PropTypes.object }
1/* src/App.jsx */
2import PropTypes from 'prop-types'
3// ... other imports
4
5const App = ({ signOut, user }) => {
6 // ...
7}
8
9App.propTypes = {
10 signOut: PropTypes.func,
11 user: PropTypes.object
12}
  1. Add this heading and button block to the top of your App component:
return ( <div style={styles.container}> <Heading level={1}>Hello {user.username}</Heading> <Button onClick={signOut}>Sign out</Button> <h2>Amplify Todos</h2> ... </div> );
1return (
2 <div style={styles.container}>
3 <Heading level={1}>Hello {user.username}</Heading>
4 <Button onClick={signOut}>Sign out</Button>
5 <h2>Amplify Todos</h2>
6 ...
7 </div>
8);
  1. Lastly, wrap your App export with the withAuthenticator Amplify UI component:
export default withAuthenticator(App);
1export default withAuthenticator(App);

Run the app to see the new authentication flow protecting the app:

npm run dev
1npm run dev

Now you should see the app load with an authentication flow allowing users to sign up and sign in.

Review complete code example

Here's all the code below:

/* src/App.js */ import { useEffect, useState } from 'react'; import PropTypes from 'prop-types' import { Amplify } from 'aws-amplify'; 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'; import awsExports from './amplifyconfiguration.json'; Amplify.configure(awsExports); 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' } }; App.propTypes = { signOut: PropTypes.func, user: PropTypes.object } export default withAuthenticator(App);
1/* src/App.js */
2import { useEffect, useState } from 'react';
3import PropTypes from 'prop-types'
4import { Amplify } from 'aws-amplify';
5import { generateClient } from 'aws-amplify/api';
6import { createTodo } from './graphql/mutations';
7import { listTodos } from './graphql/queries';
8import { withAuthenticator, Button, Heading } from '@aws-amplify/ui-react';
9import '@aws-amplify/ui-react/styles.css';
10
11import awsExports from './amplifyconfiguration.json';
12Amplify.configure(awsExports);
13
14const initialState = { name: '', description: '' };
15const client = generateClient();
16
17const App = ({ signOut, user }) => {
18 const [formState, setFormState] = useState(initialState);
19 const [todos, setTodos] = useState([]);
20
21 useEffect(() => {
22 fetchTodos();
23 }, []);
24
25 function setInput(key, value) {
26 setFormState({ ...formState, [key]: value });
27 }
28
29 async function fetchTodos() {
30 try {
31 const todoData = await client.graphql({
32 query: listTodos
33 });
34 const todos = todoData.data.listTodos.items;
35 setTodos(todos);
36 } catch (err) {
37 console.log('error fetching todos');
38 }
39 }
40
41 async function addTodo() {
42 try {
43 if (!formState.name || !formState.description) return;
44 const todo = { ...formState };
45 setTodos([...todos, todo]);
46 setFormState(initialState);
47 await client.graphql({
48 query: createTodo,
49 variables: {
50 input: todo
51 }
52 });
53 } catch (err) {
54 console.log('error creating todo:', err);
55 }
56 }
57
58 return (
59 <div style={styles.container}>
60 <Heading level={1}>Hello {user.username}</Heading>
61 <Button onClick={signOut} style={styles.button}>
62 Sign out
63 </Button>
64 <h2>Amplify Todos</h2>
65 <input
66 onChange={(event) => setInput('name', event.target.value)}
67 style={styles.input}
68 value={formState.name}
69 placeholder="Name"
70 />
71 <input
72 onChange={(event) => setInput('description', event.target.value)}
73 style={styles.input}
74 value={formState.description}
75 placeholder="Description"
76 />
77 <button style={styles.button} onClick={addTodo}>
78 Create Todo
79 </button>
80 {todos.map((todo, index) => (
81 <div key={todo.id ? todo.id : index} style={styles.todo}>
82 <p style={styles.todoName}>{todo.name}</p>
83 <p style={styles.todoDescription}>{todo.description}</p>
84 </div>
85 ))}
86 </div>
87 );
88};
89
90const styles = {
91 container: {
92 width: 400,
93 margin: '0 auto',
94 display: 'flex',
95 flexDirection: 'column',
96 justifyContent: 'center',
97 padding: 20
98 },
99 todo: { marginBottom: 15 },
100 input: {
101 border: 'none',
102 backgroundColor: '#ddd',
103 marginBottom: 10,
104 padding: 8,
105 fontSize: 18
106 },
107 todoName: { fontSize: 20, fontWeight: 'bold' },
108 todoDescription: { marginBottom: 0 },
109 button: {
110 backgroundColor: 'black',
111 color: 'white',
112 outline: 'none',
113 fontSize: 18,
114 padding: '12px 0px'
115 }
116};
117
118App.propTypes = {
119 signOut: PropTypes.func,
120 user: PropTypes.object
121}
122
123export default withAuthenticator(App);
Bonus: Use Amplify UI Primitives

You used two Amplify UI components, Heading and Button. You could also convert the rest of the app to Amplify UI components by replacing the p tags with Text, the inputs with TextFields and the divs with Views.

  1. Add the Text, TextField, View components to the imported components from Amplify UI:
import { withAuthenticator, Button, Heading, Text, TextField, View } from '@aws-amplify/ui-react';
1import {
2 withAuthenticator,
3 Button,
4 Heading,
5 Text,
6 TextField,
7 View
8} from '@aws-amplify/ui-react';
  1. Replace the p tags with Text, the inputs with TextFields and the divs with Views in your App component:
<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>
1<View style={styles.container}>
2 <Heading level={1}>Hello {user.username}</Heading>
3 <Button style={styles.button} onClick={signOut}>
4 Sign out
5 </Button>
6 <Heading level={2}>Amplify Todos</Heading>
7 <TextField
8 placeholder="Name"
9 onChange={(event) => setInput('name', event.target.value)}
10 style={styles.input}
11 defaultValue={formState.name}
12 />
13 <TextField
14 placeholder="Description"
15 onChange={(event) => setInput('description', event.target.value)}
16 style={styles.input}
17 defaultValue={formState.description}
18 />
19 <Button style={styles.button} onClick={addTodo}>
20 Create Todo
21 </Button>
22 {todos.map((todo, index) => (
23 <View key={todo.id ? todo.id : index} style={styles.todo}>
24 <Text style={styles.todoName}>{todo.name}</Text>
25 <Text style={styles.todoDescription}>{todo.description}</Text>
26 </View>
27 ))}
28</View>

Using Amplify UI components together makes it easier to manage styling across your entire app.

In this example, you used the Amplify React UI library and the withAuthenticator 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.

If you would like to build your own authentication flow instead, you can use the Auth category in Amplify Libraries for JS. Amplify's Auth category class over 20 methods including signUp, signIn, forgotPassword, and signOut that allow you full control over all aspects of the user authentication flow. Check out the complete API here.

In the next section, you'll host your app on the Amplify Console, a hosting service complete with a globally available CDN, automatic deployments, easy custom domains, and CI / CD.