How to Easily Customize The AWS Amplify Authentication UI
Tags: aws, react, gatsby, devops
Published: 2018-11-29 17:17:37

---
title: How to Easily Customize The AWS Amplify Authentication UI
published: true
description: 
tags: #aws #react #gatsby #devops
cover_image: https://thepracticaldev.s3.amazonaws.com/i/2vkoeu2fvx1gmgggjubq.png
canonical_url: https://blog.kylegalbraith.com/2018/11/29/how-to-easily-customize-the-aws-amplify-authentication-ui/  
---
For [parler.io](https://parler.io) I have been experimenting with adding authentication to the project. This would allow conversions to be tied to users and enable a slew of other features as well.

During my experimenting I have been reading a lot about [AWS Amplify](https://aws-amplify.github.io/). It is a library that wraps multiple AWS services and allows you to focus on building mobile and web applications at scale on Amazon Web Services.

It makes adding various categories of features much simpler. Need authentication? There is a module for that. What about storage? Yup, there is one for that as well.

Amplify is meant to make stitching together AWS services a seamless process. A simple command line call can provide all of the services you need in your AWS account to handle authentication.

The Amplify Framework makes creating scalable mobile and web applications in AWS a simplified process. In this post, I am going to walk through how I used AWS Amplify to add authentication to Parler and how I customized the user interface components to fit my needs.

### Getting Started
Amplify is an AWS provided framework. To get started we must install and configure the CLI for Amplify.

```bash
$ npm install -g @aws-amplify/cli
```

If you don't have the [AWS CLI](https://aws.amazon.com/cli/) installed and configured you are going to need to configure the Amplify CLI. If you already have the AWS CLI configured you don't need to configure the Amplify one as well.

```bash
# only run this configure if you don't have the AWS CLI
$ amplify configure
```

Once the Amplify CLI is installed we can begin adding modules to our mobile or web application.

For my project, I am using [Gatsby](https://www.gatsbyjs.org/) to build out the web application. This is a modern static site generator that can be used to quickly create static websites, blogs, portfolios, and even web applications. Since Gatsby is built on top of React we can use all of the same ideas from React in Gatsby.

Let's initialize and configure our initial Amplify setup for a React web application.

### Initializing Amplify
Now that we have the CLI installed globally we can initialize Amplify inside of our React app with one command line call.

```bash
# run this from the root directory of your application
$ amplify init
```
This command will initialize our AWS configuration and create a configuration file at the root of our application. This command will not provision any services in our AWS account, but it lays the groundwork for us to do so.

### Adding authentication to our application
Now that we have initialized the framework in our application we can start adding modules. For this blog post, we are going to add the `authentication` module to our application.

We can do this with another call on our command line.

```bash
$ amplify add auth
```

This command will walk us through a series of questions. Each question is configuring the authentication for our application. If you are unsure what configuration you need, go ahead and select `Yes, use the default configuration` for the first question. You can always come back and reconfigure these settings by running the command `amplify update auth`.

We now have the authentication module configured for our application. But, we still need to deploy this configuration to our AWS account. Lucky for us, this is handled by the Amplify CLI as well.

```bash
$ amplify push
```

This will create and deploy the necessary changes to our AWS account to support our authentication module. With the default settings, this will provision AWS Cognito to handle authentication into our application.

When the deployment is complete we will have a new file in our source directory, `aws-exports.js`. This file represents the infrastructure inside of our AWS account to support our Amplify project.

### Using Amplify with React
The Amplify framework has been added, we configured authentication, and we provisioned the necessary AWS services to support our application. Now it's time we set up our React/Gatsby application to leverage the framework.

For the purpose of this blog post, we are going to assume we have an `App` component that is the main entry point for our application. We are also going to assume that you can't access the application without being authenticated first.

Here is what our initial `App` component is going to look like. It is served at the `/app` route via a Gatsby configuration. Right now it is wide open to the world, no authentication is needed.

```javascript
import React from "react";

class App extends React.Component {
  constructor(props, context) {
    super(props, context);
  }

  render() {
    return (
      <div>
        <h1>Internal App</h1>
      </div>
    );
  }
}

export default App;
```

With me so far? Great. Now we want to put our application behind the authentication module we added via Amplify. To do that we install two more libraries in our project.

```bash
$ npm install aws-amplify aws-amplify-react
```

Now that we have added these two libraries we can quickly add authentication to our application. First, we need to configure Amplify inside our `App` component. Then we can use a higher order component (HOC), `withAuthenticator`, specifically created for React applications. This component adds all of the logic to put our `App` component behind authentication. It also includes all of the UI pieces we need to log users in, sign up new users, and handle flows like confirming an account and resetting a password.

Let's take a look at what these changes look like in our `App` component.

```javascript

import React from "react";
import Amplify from "aws-amplify";
import { withAuthenticator } from "aws-amplify-react";
import config from "../../aws-exports";
Amplify.configure(config);

class App extends React.Component {
  constructor(props, context) {
    super(props, context);
  }

  render() {
    return (
      <div>
        <h1>Internal App</h1>
      </div>
    );
  }
}

export default withAuthenticator(App, true);
```

Just like that we now have authentication added to our React application that is built with Gatsby. If we run `gatsby develop` from our command line and check out our changes locally we should be able to see the default login prompt provided by Amplify.

![default Amplify UI](https://thepracticaldev.s3.amazonaws.com/i/3j7d22pj3vu1osy2uft5.PNG)

Pretty slick right? With a few command line operations, we have authentication incorporated into our application. All of the AWS services needed to support our app are provisioned and continuously maintained by the Amplify Framework. 

This is all fantastic, but for Parler, I also wanted the ability to customize the UI pieces that Amplify provides. These pre-configured UI components are great for getting started but I wanted to add my own style to them using Tailwind CSS.

So now let's explore how to customize the authentication UI of Amplify by overriding the default components like `SignIn` with our own `CustomSignIn` component.

### Customizing the Amplify authentication UI
To customize the look and feel of the Amplify authentication module we need to define our own components for the UI pieces we want to change.

For example, the login UI is handled by a component inside of Amplify called `SignIn`, you can see the full source code of that module [here](https://github.com/aws-amplify/amplify-js/blob/master/packages/aws-amplify-react/src/Auth/SignIn.jsx).

What we are going to do next is define our own component, `CustomSignIn`, that is going to extend the `SignIn` component from Amplify. This allows us to use all of the logic already built into the parent component but define our own UI. Let's take a look at what `CustomSignIn` looks like.

```javascript
import React from "react";
import { SignIn } from "aws-amplify-react";

export class CustomSignIn extends SignIn {
  constructor(props) {
    super(props);
    this._validAuthStates = ["signIn", "signedOut", "signedUp"];
  }

  showComponent(theme) {
    return (
      <div className="mx-auto w-full max-w-xs">
        <form className="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4">
          <div className="mb-4">
            <label
              className="block text-grey-darker text-sm font-bold mb-2"
              htmlFor="username"
            >
              Username
            </label>
            <input
              className="shadow appearance-none border rounded w-full py-2 px-3 text-grey-darker leading-tight focus:outline-none focus:shadow-outline"
              id="username"
              key="username"
              name="username"
              onChange={this.handleInputChange}
              type="text"
              placeholder="Username"
            />
          </div>
          <div className="mb-6">
            <label
              className="block text-grey-darker text-sm font-bold mb-2"
              htmlFor="password"
            >
              Password
            </label>
            <input
              className="shadow appearance-none border rounded w-full py-2 px-3 text-grey-darker mb-3 leading-tight focus:outline-none focus:shadow-outline"
              id="password"
              key="password"
              name="password"
              onChange={this.handleInputChange}
              type="password"
              placeholder="******************"
            />
            <p className="text-grey-dark text-xs">
              Forgot your password?{" "}
              <a
                className="text-indigo cursor-pointer hover:text-indigo-darker"
                onClick={() => super.changeState("forgotPassword")}
              >
                Reset Password
              </a>
            </p>
          </div>
          <div className="flex items-center justify-between">
            <button
              className="bg-blue hover:bg-blue-dark text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline"
              type="button"
              onClick={() => super.signIn()}
            >
              Login
            </button>
            <p className="text-grey-dark text-xs">
              No Account?{" "}
              <a
                className="text-indigo cursor-pointer hover:text-indigo-darker"
                onClick={() => super.changeState("signUp")}
              >
                Create account
              </a>
            </p>
          </div>
        </form>
      </div>
    );
  }
}

```

With `CustomSignIn` we are extending the `SignIn` component from `aws-amplify-react`. This is so that we can override the `showComponent` method but still use the parent class functions like `changeState` and `signIn`.

Notice that we are not overriding the `render` method but `showComponent` instead. This is because the parent `SignIn` component defines the UI inside of that function. Therefore, to show our UI we need to override it in our component.

Inside of our constructor we see the following statement.

```javascript
this._validAuthStates = ["signIn", "signedOut", "signedUp"];
```

Amplify uses `authState` to track which authentication state is currently active. The custom components we define can state which auth states are valid for this component. Since we are on the login/sign in view, we only want to render our custom UI if `authState` equals `signIn`, `signedOut`, or `signedUp`. That is all of the magic sauce happening to show our UI over the default Amplify provided UI.

We extend the `SignIn` component, override the `showComponent` function, check the `authState` and show our UI if the state is the one that we are looking for. 

Pretty slick right?

Diving into the custom UI a bit we see the "Create Account" button makes a call to `super.changeState("signUp")` when its clicked. This is a function defined in the parent component we are extending. It updates the `authState` to `signUp` and the `SignUp` component is rendered. We could, of course, customize this component as well following the same process we used to create `CustomSignIn`.

The only other change we need to make now is back out in our `App` component. Instead of using the `withAuthenticator` HOC provided by Amplify we are going to use the `Authenticator` component directly.

To make things clearer we are going to define a new component, `AppWithAuth`, that wraps our `App` component and makes use of the `Authenticator` component directly.

```javascript
import React from "react";
import { SignIn } from "aws-amplify-react";
import config from "../../aws-exports";
import { CustomSignIn } from "../Login";
import App from "../App";
import { Authenticator } from "aws-amplify-react/dist/Auth";

class AppWithAuth extends React.Component {
  constructor(props, context) {
    super(props, context);
  }

  render() {
    return (
      <div>
        <Authenticator hide={[SignIn]} amplifyConfig={config}>
          <CustomSignIn />
          <App />
        </Authenticator>
      </div>
    );
  }
}

export default AppWithAuth;
```
Now our `App` component will receive the `authState`, just like our other components, inside of its `render` method. If we check the state inside of that method we can show our `App` component only when we are signed in. Let's take a look at our new `App` component code.

```javascript
import React from "react";

class App extends React.Component {
  constructor(props, context) {
    super(props, context);
  }

  render() {
    if (this.props.authState == "signedIn") {
      return (
        <div>
          <h1>Internal App</h1>
        </div>
      );
    } else {
      return null;
    }
  }
}

export default App;
```

Now our `App` component is very minimal. In fact, the only notion we have of Amplify here is checking our `authState` which determines whether or not we should render this component.

![parler.io authentication with Amplify](https://thepracticaldev.s3.amazonaws.com/i/8un3uyz8q5eimo034gbp.gif)

Just like that, we have added authentication to our application using the Amplify Framework. We have also customized the components of Amplify to give our own look, feel, and logic if we need it.

### Conclusion
The Amplify Framework is an awesome new tool in our AWS toolbox. We demonstrated here that we can add authentication to any web or mobile application with just a few CLI commands. We can then deploy the AWS services that back modules like authentication with a simple `push` call.

But sometimes we want to add our own style to these types of frameworks. Not a problem. We showed that we can extend the base components inside of Amplify to create our user interfaces as well as hide the ones we don't care about.

Amplify continues to evolve and consists of many more modules like hosting, api, auth, and even storage. All key modules and AWS services that are important to most web applications. In addition, they also just announced [Amplify Console](https://aws.amazon.com/about-aws/whats-new/2018/11/announcing-aws-amplify-console/) which contains a global CDN to host your applications as well as a CI/CD Pipeline.

If you have any questions about this post or Amplify, feel free to drop me a comment below.

### Are you hungry to learn even more about Amazon Web Services?

If you are looking to begin your AWS journey but feel lost on where to start, consider [checking out my course](https://www.kylegalbraith.com/learn-aws). We focus on hosting, securing, and deploying static websites on AWS. Allowing us to learn over 6 different AWS services as we are using them. After you have mastered the basics there we can then dive into **two bonus chapters** to cover more advanced topics like Infrastructure as Code and Continuous Deployment.