How to Setup Authentication Fast with AWS Amplify
Published: 2022-08-12 14:57:00

In this article, we will build a blog application using AWS Amplify which allows users to sign in and start blogging right after signup has been completed.

{% embed https://youtu.be/TqSxBTQtgJk %}

AWS Amplify enables front-end and mobile developers to easily build and deploy full-stack web and mobile applications powered by AWS. Authentication is one of the most vital features in an application as it determines the identity of a user. Recently, the Amplify team added a new feature to the Amplify authentication flow. The feature allows users to automatically sign in to the app immediately after signing up.

## Pre-requisites

To follow along, you need to have:

- [AWS account](https://aws.amazon.com/)
- Knowledge of JavaScript and React
- A code editor (VS Code preferably)
- Nodejs >=v14 installed
- AWS Amplify CLI installed. Run this command if you don’t have it installed
     npm install -g @aws-amplify/cli
- Authorized AWS Amplify CLI
     amplify configure

The complete code for this post is on this [Github](https://github.com/Djcodebeast/react-amplify-auth) repository.

## Setting up the React project

We’ll be building a simple React application for this post. Run this command to scaffold a new React project:
```bash
     npx create-react-app react-amplify-auth
```
When it’s done installing, change directory to the react project, and open the folder with your code editor.
```bash
    cd react-amplify-auth
    code .
```
Let’s go ahead to configure amplify for the project and create a user profile.

## Setting up Amplify

We have setup a user profile for the amplify project, let’s now initialize amplify in the project. Run this command on your terminal.

     amplify init

Select these options:
```bash
    ➜  react-amplify-auth git:(master) amplify init
    Note: It is recommended to run this command from the root of your app directory
    ? Enter a name for the project reactamplifyauth
    The following configuration will be applied:
    
    Project information
    | Name: reactamplifyauth
    | Environment: dev
    | Default editor: Visual Studio Code
    | App type: javascript
    | Javascript framework: react
    | Source Directory Path: src
    | Distribution Directory Path: build
    | Build Command: npm run-script build
    | Start Command: npm run-script start
    
    ? Initialize the project with the above configuration? Yes
    Using default provider  awscloudformation
    ? Select the authentication method you want to use: AWS profile
    
    For more information on AWS Profiles, see:
    <https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-profiles.html>
    
    ? Please choose the profile you want to use react-amplify-auth
```
You will see some installation logs and when it’s done, you will see a message like this:
```bash
    CREATE_COMPLETE amplify-reactamplifyauth-dev-133741 AWS::CloudFormation::Stack Wed Aug 03 2022 13:38:19 GMT+0100 (West Africa Standard Time) 
    ✔ Successfully created initial AWS cloud resources for deployments.
    ✔ Help improve Amplify CLI by sharing non sensitive configurations on failures (y/N) · yes
    ✔ Initialized provider successfully.
    ✅ Initialized your environment successfully.
    
    Your project has been successfully initialized and connected to the cloud!
```
If you’ve gotten to this point, you’re Awesome 🙌🏾 An amplify folder has been created and the directory structure looks like this:
```bash
    .
    ├── #current-cloud-backend
    │   ├── amplify-meta.json
    │   ├── awscloudformation
    │   │   └── build
    │   │       └── root-cloudformation-stack.json
    │   └── tags.json
    ├── README.md
    ├── backend
    │   ├── amplify-meta.json
    │   ├── awscloudformation
    │   │   └── build
    │   │       └── root-cloudformation-stack.json
    │   ├── backend-config.json
    │   ├── tags.json
    │   └── types
    │       └── amplify-dependent-resources-ref.d.ts
    ├── cli.json
    ├── hooks
    │   ├── README.md
    │   ├── post-push.sh.sample
    │   └── pre-push.js.sample
    └── team-provider-info.json
```
Fantastic! Let’s now go ahead to add the authentication module to our React application.


## Setting up Amplify Auth

We’ve successfully initialized amplify and connected our amplify project to the cloud. Let’s now create the authentication service. Run this command on your terminal:
```bash
      amplify add auth
```
You will be prompted with these options:
```bash
    You will be prompted with these options:
    
    Using service: Cognito, provided by: awscloudformation
    
    The current configured provider is Amazon Cognito. 
    
    Do you want to use the deUPDATE_COMPLETE amplify-reactamplifyauth-dev-133741 AWS::CloudFormation::Stack Wed Aug 03 2022 15:03:45 GMT+0100 (West Africa Standard Time) 
    ✔ All resources are updated in the cloudfault 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.
    ✅ Successfully added auth resource reactamplifyauth963ad60d locally
```
This adds a `auth/reactamplifyauth963ad60d` to the amplify backend directory. Now let’s go ahead and push the auth service to amplify cloud. Run this command on your terminal:
```bash
     amplify push
```
You will see deploy logs and this message at the end

    UPDATE_COMPLETE amplify-reactamplifyauth-dev-133741 AWS::CloudFormation::Stack Wed Aug 03 2022 15:03:45 GMT+0100 (West Africa Standard Time) 
    ✔ All resources are updated in the cloud

Head over to your amplify studio with `amplify console` and you will see something like this:

![Amplify studio](https://paper-attachments.dropbox.com/s_A211447B2040C9FDDC8AC2902AA60C23C61B7B37A1EFA135D349CF42BE2C0A8D_1659536094596_Screenshot+2022-08-03+at+15.14.41.png)


You can see Categories added: Authentication. Awesome. Let’s now integrate authentication to the frontend.

## Setting up GraphQL API

We will be building a simple blog Posts API. Run this command to create an amplify api
```bash
amplify add api
```
Select these options:
```bash
➜  react-amplify-auth git:(master) ✗ amplify add api
? Select from one of the below mentioned services: GraphQL
? Here is the GraphQL API that we will create. Select a setting to edit or continue Continue
? Choose a schema template: Single object with fields (e.g., “Todo” with ID, name, description)
```
Update the `schema.graphql` with these lines of code:
```graphql
    @auth(rules: [{allow: owner}])
    type Posts @model {
      id: ID!
      title: String!
      description: String
      author: String
    }
```
Next, run
```bash
     amplify push --y
```
This will create

- a AppSync GraphQL API,
- a `graphql` folder in the app `src` folder that consists of local GraphQL operations
- a DynamoDB table for Posts

At this point, we have successfully created the backend for the project. Let’s go ahead to create the user interface with React.

## Setting up Auth in React

Firstly, delete all the files except `App.js`, `index.js`, and `aws-exports.js` . Install these dependencies:
```bash
     npm i aws-amplify @aws-amplify/ui-react tailwindcss postcss autoprefixer
```
Update your `index.js` to look like this:
```javascript
    //src/index.js
    import React from 'react';
    import ReactDOM from 'react-dom/client';
    import App from './App';
    import './index.css';
    import Amplify from 'aws-amplify';
    import config from './aws-exports';
    Amplify.configure(config);
    
    const root = ReactDOM.createRoot(document.getElementById('root'));
    root.render(
      <React.StrictMode>
        <App />
      </React.StrictMode>
    );
```
Create an `index.css` and add these lines of code add tailwind styles to the project.
```css
    /* src/index.css */
    @tailwind base;
    @tailwind components;
    @tailwind utilities;
```
Finally, update the content of `tailwind.config.js` to this.
```javascript
    //tailwind.config.js
    module.exports = {
      content: [
        "./src/**/*.{js,jsx,ts,tsx}",
      ],
      theme: {
        extend: {},
      },
      plugins: [],
    } 
```
Let’s go ahead and create the authentication flow. Create a `Signup.js` and add these lines of code.
```javascript
    //src/Signup.js
    import { Auth } from 'aws-amplify';
    import { useState } from 'react';
    import { useNavigate } from 'react-router-dom';
    
    const initialFormState = { username: "", password: "", email: "" }
    const Signup = () => {
        const [formData, setFormData] = useState(initialFormState);
        const navigate = useNavigate();
        async function signUp() {
            try {
                const { user } = await Auth.signUp({
                    username: formData.username,
                    password: formData.password,
                    attributes: {
                        email: formData.email,
                    },
                    autoSignIn: { // optional - enables auto sign in after user is confirmed
                        enabled: true,
                    }
                });
                console.log(user);
                localStorage.setItem('username', user.username);
                navigate('/confirm', { replace: true })
    
            } catch (error) {
                console.log('error signing up:', error);
            }
        }
        return ()
```
Here, we import `Auth` from `aws-amplify`. It has all the methods we will need to implement the auth flow. Methods such as `signUp`, `confirmSignUp`, `signOut`, and a host of others. We went ahead to create an `initialFormState` object with `username`, `password`, and `email` as keys, they all have an empty string as initial values this will represent the initial state values for the form data we will be creating next.
Next, in the `signUp` function, we destructure the data retrieved from the `Auth.signup()` method into a `user` object. The `Auth.signUp()` method accepts a username, password, and attributes and any other optional parameters. Here, we included `autoSignIn` and enabled it. The `autoSignIn` method is a new feature that allows the user to sign in automatically without requiring `Auth.signIn` to login.
Finally, we save the `username` from the `user` object to localStorage. Add the following lines of code:
```jsx
<div className="bg-white">
    <div className="grid max-w-screen-xl h-screen text-black m-auto place-content-center">
        <div className="w-[30rem] space-y-6">
            <div className="flex flex-col">
                <label> Username </label>
                <input
                    onChange={e => setFormData({ ...formData, 'username': e.target.value })}
                    placeholder="username"
                    value={formData.username}
                    type="text"
                    className="border border-sky-500 p-2 rounded w-full"
                />
            </div>

            <div className="flex flex-col">
                <label>Password  </label>
                <input
                    onChange={e => setFormData({ ...formData, 'password': e.target.value })}
                    placeholder="pasword"
                    value={formData.password}
                    type="password"
                    className="border p-2 rounded border-sky-500"
                />
            </div>

            <div className="flex flex-col">
                <label>Email </label>
                <input
                    onChange={e => setFormData({ ...formData, 'email': e.target.value })}
                    placeholder="email"
                    value={formData.email}
                    type="email"
                    className="border p-2 rounded border-sky-500"
                />
            </div>

            <div>
                <button className="border-none bg-sky-700 text-white p-2 mt-4 rounded m-auto" onClick={signUp}> Sign up </button>
            </div>
        </div>
    </div>
</div>
```

Here, we have `inputs`. On each field, we use an `onChange` event and assign the value of the field to the `formData` object.
Let’s go ahead and create the confirm sign up screen. Create a `confirmSignup.js` and add these lines of code:
```javascript

    //src/confirmSignup.js
    import { useState } from 'react';
    import { Auth } from 'aws-amplify';
    import { useNavigate } from 'react-router-dom';
    
    const initialFormState = { code: "" }
    const ConfirmSignup = () => {
        const [formData, setFormData] = useState(initialFormState);
        const navigate = useNavigate();
        const username = localStorage.getItem('username')
        const code = formData.code
        async function confirmSignUp() {
            try {
                await Auth.confirmSignUp(username, code);
                localStorage.clear();
                navigate('/posts', { replace: true })
            } catch (error) {
                console.log('error confirming sign up', error);
            }
        }
        return (
            <div className="grid max-w-screen-xl h-screen text-black m-auto place-content-center">
                <div className="w-[30rem] space-y-6">
                    <label htmlFor='Confirmation Code'> Enter the confirmation code sent to your email </label>
                    <input
                        onChange={e => setFormData({ ...formData, 'code': e.target.value })}
                        placeholder="code"
                        value={formData.code}
                        type="text"
                        className="border border-sky-500 p-2 rounded w-full shadow"
                    />
                </div>
                <button className="border-2 bg-sky-700 border-none text-white p-2 mt-4 rounded m-auto" onClick={confirmSignUp}> Confirm</button>
            </div>
        )
    }
    
    export default ConfirmSignup
```
Here, the `Auth.confirmSigUp()` accepts a `username` and `code`. We retrieve the `username` from `localStorage` and the code from the value of the input.

Go ahead and create a `Home.js` and add these lines of code:
```javascript

    //src/Home.js
    import { useNavigate } from "react-router-dom"
    
    const Home = () => {
        const navigate = useNavigate()
        const redirectToSignup = () => {
            navigate('/signup')
        }
        return (
            <div className="bg-black">
    
                <div className="grid max-w-screen-xl h-screen text-white m-auto place-content-center">
                    <h2 className="mb-8 text-4xl"> Amplify Posts app</h2>
                    <button className="border-2 p-2" onClick={redirectToSignup}> Create Account</button>
                </div>
            </div>
        )
    }
    
    export default Home
```
In this page, we just have a button that’ll redirect us to the signup screen.
Update the `App.js` with these lines of code:
```javascript

    //src/App.js
    import { BrowserRouter as Router, Routes, Route } from "react-router-dom"
    import Signup from './Signup'
    import Posts from './Post'
    import Confirm from './confirmSignup'
    import Home from "./Home"
    
    const App = () => {
      return (
        <div>
          <Router>
            <Routes>
              <Route path="/" element={<Home />}> Home</Route>
              <Route path="/signup" element={<Signup />}> Signup </Route>
              <Route path="/confirm" element={<Confirm />}></Route>
              <Route path="/posts" element={<Posts />}> </Route>
            </Routes>
          </Router>
        </div>
      )
    }
    
    export default App
```
Here, we define the routes for our application. So far, we’ve created `Home`, `Signup`, and `Confirm` routes, we should go ahead to create the Posts screen.
Create a `src/Posts.js` and add these lines of code:

```javascript
    //src/Posts.js
    import "@aws-amplify/ui-react/styles.css";
    import { Auth, API } from "aws-amplify";
    import { useState, useEffect } from 'react'
    import { listPosts } from "./graphql/queries";
    import { createPosts as createPostsMutation } from "./graphql/mutations";
    import { useNavigate } from "react-router-dom";
    
    const initialFormState = { title: "", author: "", description: "" }
    function Posts() {
        const [user, setUser] = useState()
        const [posts, setPosts] = useState([]);
        const [formData, setFormData] = useState(initialFormState);
        const navigate = useNavigate()
    
        async function fetchUser() {
            Auth.currentAuthenticatedUser({
                bypassCache: true  // Optional, By default is false. If set to true, this call will send a request to Cognito to get the latest user data
            })
                .then(user => {
                    console.log(user)
                    setUser(user)
                })
                .catch(err => console.log(err));
        }
    
        async function fetchPosts() {
            const apiData = await API.graphql({ query: listPosts });
            setPosts(apiData.data.listPosts.items);
        }
    
        async function createPost() {
            if (!formData.title || !formData.description || !formData.author) return;
            await API.graphql({ query: createPostsMutation, variables: { input: formData } });
            setPosts([...posts, formData]);
            setFormData(initialFormState);
        }
    
        async function signOut() {
            try {
                await Auth.signOut({ global: true });
                navigate('/signup', { replace: true })
            } catch (error) {
                console.log('error signing out: ', error);
            }
        }
        useEffect(() => {
            fetchUser()
            fetchPosts()
        }, [])
        return ()
```
Let’s go through this code snippet. The `posts` array will hold the posts we are fetching from the graphql API. While `formData` will hold the values of the inputs. We also have these functions:

- `fetchPosts` - This function uses the API class to send a query to the GraphQL API and retrieve a list of posts.
- `createPost` - This function also uses the API class to send a mutation to the GraphQL API. Here, we pass in the variables needed for a GraphQL mutation to create a new post with the form data. Let’s go ahead and build the UI.
- `fetchUser()` - This uses the `Auth.currentAuthenticatedUser()` method to check if the user has been authenticated. If yes, it returns the user object.
- `signOut`- This function uses the `Auth.signOut` method to sign out an authenticated user.

Let’s go ahead to implement the ui. Add these lines of jsx
```jsx

    <div className=" m-auto p-12">
        <div className=" space-y-6  w-[30rem] m-auto">
            <div className="flex flex-col">
                <input
                    onChange={e => setFormData({ ...formData, 'title': e.target.value })}
                    placeholder="Post title"
                    value={formData.title}
                    type="text"
                    className="border border-sky-500 p-2 rounded w-full"
                />
            </div>

            <div className="flex flex-col">
                <input
                    onChange={e => setFormData({ ...formData, 'author': e.target.value })}
                    placeholder="Post author"
                    value={formData.author}
                    type="text"
                    className="border border-sky-500 p-2 rounded w-full"
                />
            </div>

            <div className="flex flex-col">
                <textarea
                    onChange={e => setFormData({ ...formData, 'description': e.target.value })}
                    placeholder="Post description"
                    value={formData.description}
                    className="border border-sky-500 p-2 rounded w-full"
                />
            </div>

            <button className="border-none bg-sky-700 text-white p-2 mt-4 rounded m-auto" onClick={createPost}>Create Post</button>
        </div>
    </div>
```
Finally, in this code snippet, we have a button that invokes the `createPost()` function. Let’s move on and create the view for the posts.

```jsx
    <div className="mt-20">
    
    <h1 className="text-3xl font-semibold "> Welcome {user && (<span>{user.username}</span>)} </h1>
            <h2>Here are your posts:</h2>
            {posts === 0 ?
                (

                    <div>
                        <h3> They are no posts</h3>
                    </div>
                ) :

                posts?.map(post => (
                    <div key={post.id || post.title} className="my-10">
                        <h2 className="text-xl font-semibold">Title: {post.title}</h2>
                        <p className="font-bold text-lg"> Author: {post.author}</p>
                        <p>Message: {post.description}</p>
                    </div>
                ))
            }
        </div>

        <button className="border-2 bg-red-500 border-none text-white p-2 mt-8 rounded m-auto" onClick={signOut}> Signout</button>
    </div>
```
Earlier, we created a `posts` array that contains the posts fetched from the GraphQL API. Here, we map through the `posts` and render the title, description, and author information. We also have a signOut button, we use the `signOut` function from Authenticator here.
If you’ve noticed when you Create an account and pass the verification process, you’re automatically logged in to the app. This feature was added recently, and we can get to use it now! Behind the scenes, the confirm sign-up listener initiates auto sign-in once the account is confirmed. It happens if `autoSignIn` is set to true in params for `SignUp` function. They are three different ways you can choose to auto sign in which are `code`, `link`, or `autoConfirm`. `code` is the default.
Code implementation uses a hub listening event. Once the user confirms the account with code, it dispatches a listening event. The `SignUp` function is listening for this event to initiate `SignIn` process.
The Link implementation uses polling to sign users in since there is no explicit way to know if the user clicked the link. Polling is done within 3 minutes (time verification link is valid) every 5 seconds.
So that’s it, you should have something like this:

![amplify post app home view](https://paper-attachments.dropbox.com/s_A211447B2040C9FDDC8AC2902AA60C23C61B7B37A1EFA135D349CF42BE2C0A8D_1660306005071_Screenshot+2022-08-11+at+20.08.18.png)

![sign up view](https://paper-attachments.dropbox.com/s_A211447B2040C9FDDC8AC2902AA60C23C61B7B37A1EFA135D349CF42BE2C0A8D_1660306034345_Screenshot+2022-08-11+at+20.10.22.png)

![confirm code view](https://paper-attachments.dropbox.com/s_A211447B2040C9FDDC8AC2902AA60C23C61B7B37A1EFA135D349CF42BE2C0A8D_1660306064919_Screenshot+2022-08-11+at+20.11.36.png)

![posts view](https://paper-attachments.dropbox.com/s_A211447B2040C9FDDC8AC2902AA60C23C61B7B37A1EFA135D349CF42BE2C0A8D_1660306092677_Screenshot+2022-08-12+at+10.35.01.png)

## Conclusion

Amplify makes authentication easy while using Amazon Cognito as an Authentication provider. In this post, we learned how to configure and implement authentication with the Amplify framework.