AWS Amplify: Using Existing Auth and API Resources
Tags: awsamplify, aws, react, serverless
Published: 2020-05-09 07:28:05

AWS Amplify is both a CLI toolchain and suite of methods that enable frontend developers to quickly create cloud-based backends while having the scaffolding and best-practices taken care of for them.

For example, if wanting to add a user signup flow to an app, entering the following command will provide guided prompts to accept a default configuration. You can even configure advanced settings.

```sh
amplify add auth
```

Behind the scenes, this sets up an AWS Cognito Pool, as well as an Identity Pool so that user attributes can be stored.

Additionally, if wanting to do the same, but for a RESTful API the following command will trigger a guided prompt to get that configured in your project as well.

```sh
amplify add auth
```

However, for many frontend developers, these resources may be already configured ahead of time by yourself or backend members of your team. Fortunately, the Amplify package allows us to bring in those resources as well.

In this post, let's explore how we can configure a Cognito Pool for user authorization, a RESTful API with ApiGateway, and finally connect those in our application using the AWS Amplify library.

---

# Configuring our Cognito Pool

> 🚨 I'll be showing off actual secret keys during this post for the sake of learning, however I'll be sure to destroy these resources by the time this post goes live 😉

Let's first create our Cognito Pool by logging into the AWS Console and navigating to the [AWS Cognito homepage](https://console.aws.amazon.com/cognito/home). From there, we'll select the option **"Manage User Pools"**, followed by **"Create a user pool"** in the top-right corner.

The next screen will ask us to give our user pool a name. I'm going to name mine `demopool` and select "Review defaults".

![aws cognito pool naming page](https://dev-to-uploads.s3.amazonaws.com/i/woj5p1scmgxfeqm7iu4l.png)

From here, we can select any of the items we'd like to tweak before creating our user pool.

For this example, I'll select the email configuration section, and change the email message that my users will be sent when verifying their signup.

![cognito configuration page](https://dev-to-uploads.s3.amazonaws.com/i/g1f6ckqyin7tqn71tics.png)

![email verification message](https://dev-to-uploads.s3.amazonaws.com/i/y1ens6guyam9bzr8a8vf.png)

> 🚨When done, remember to click **"Save Changes"** at the bottom of the page.

Additionally, feel free to add tags, modify the password settings, etc. However, before selecting **"Create pool"** at the bottom of the page, we'll need to add and configure an App Client so that Amplify can successfully authenticate our frontend. So either from the left panel, or the main area, select **"App Clients"**, and next select **"Add an app client"**.

All we'll need to do here is give our app client a name, and **uncheck** "Generate client secret". Afterwards, we can select "Create app client" at the bottom.

![configure client app](https://dev-to-uploads.s3.amazonaws.com/i/81551yx8a0be2phfo8v9.png)

When done, note that we'll need the generated **pool id**, as well as the **app client id** that we created earlier, so copy those over for easy access for the time being, and we'll switch over to our project

# Setting up an Amplify Project

Chances are, you have the Amplify CLI already installed and configured on your machine, if not, please refer to [this video](https://youtu.be/fWbM5DLh25U) on how to get it setup.

From there, let's open up our terminal, and create a new react application and after changing into the project directory, we'll add a few extra dependencies:

```sh
npx create-react-app custom-amplify-demo --use-npm
```
```sh
cd custom-amplify-demo && npm i aws-amplify @aws-amplify/ui-react
```

With our project scaffolded, and dependencies installed, let's configure Amplify to use our custom auth resource.

In `src/App.js`, replace the current code with the following:

```jsx
import React from "react";
import "./App.css";
import Amplify from "aws-amplify";
import { withAuthenticator, AmplifySignOut } from "@aws-amplify/ui-react";

Amplify.configure({
  Auth: {
    region: "us-east-1",
    userPoolId: "us-east-1_T2ZfRqx59",
    userPoolWebClientId: "61l5lp494qnb60ek6h0ohonspp",
  },
});

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <p>welcome!</p>
        <AmplifySignOut />
      </header>
    </div>
  );
}

export default withAuthenticator(App);
```
Starting with our `import`s, we're first bringing in the `Amplify` library so that we can talk to our backend resource. Next, we import a higher-ordered-component called `withAuthenticator` as well as a component to let us sign out of the app.

From there, we are configuring the Amplify library with the credentials needed for our Cognito Pool. Lastly, we are creating a component that simply renders out a "welcome!" message along with our sign out button. Save the app, and run `npm run start` so that our app starts on `localhost:3000`.

Just like that, you should now see the application showing a fully-featured signup form that is absolute positioned on the webpage.

![signup form to create an account](https://dev-to-uploads.s3.amazonaws.com/i/qmfrwuljnulxr36x286t.png)

Go ahead and sign up for an account. After signing up, check your email for for the verification code. If all went well, your message should contain the content we provided in our Cognito Pool.

![Code Verification Email](https://dev-to-uploads.s3.amazonaws.com/i/sun8ytjnk4usixvvkfce.png)

Go ahead and take the verification code at the bottom of the email, and use it as input for the signup form. Once done, you should be automatically routed to the application.

🎉We did it🎉

It's worth noting that everything we did in this example could have been done using the CLI that Amplify provides, however the flexibility to use custom resources is a great feature to have regardless!

---

# Configuring Our API Gateway

Our next task is go create a custom REST endpoint. To do this, we'll first create a cloud function (AWS lambda) and assign it to a GET method in API Gateway. This can be particularly useful when wanting to separate your frontend with your infrastructure-as-code, where it's common to create this with Cloudformation, Terraform, or Serverless Framework.

To begin, we'll go back into the AWS Console, and navigate to the [AWS Lambda creation page](https://console.aws.amazon.com/lambda/home?region=us-east-1#/create/function).

> 🚨 Make sure you are in the same region as your Cognito pool

On this page, we'll give our lambda (cloud function) a name and select create function. Optionally, feel free to adjust the runtime and associated policy if that better suits your use case.

![create a lambda](https://dev-to-uploads.s3.amazonaws.com/i/sgukbjt406bclcv6y06f.png)

On the following page, we can adjust the response that we'd like to send back to our users when they hit our to-be-created endpoint, and select save.

![configure a lambda with a custom response](https://dev-to-uploads.s3.amazonaws.com/i/8d716yug53sqx4nujut6.png)

At this point, we have our lambda created, but we have no way to invoke it. Let's change that by adding API Gateway as a trigger. On the same page, select the **Designer** dropdown arrow located above the **Function code** section. Next select **add trigger**, and choose API Gateway from the select field.

From here, we'll have to configure our API. For this example, we're going to make this a REST API, that will require an API key to use with no additional settings.

![configure API Gateway](https://dev-to-uploads.s3.amazonaws.com/i/eg2pz8oxop66aw2iw4ov.png)

If all went well, we should get a success message at the top of our page, along with our endpoint and API key at the bottom.

![API Verification Page](https://dev-to-uploads.s3.amazonaws.com/i/h2vx1amsc8xbkz17poek.png)

As is, our API would work as long as we pass in the API Key, however, because we're going to calling our API from the browser on a different domain, we have to enable CORS. Also, API Gateway setup a generic `ANY` method, but Amplify only supports `GET`,`PUT`,`DELETE`,`POST`. Let's click the resource name to be taken to API Gateway, where we will do he following: 

1. Configure our API with a single `GET` method.
2. Assign the lambda we created earlier to that method.
3. Protect this route with the API key we created earlier.
4. Enable CORS on our endpoint
5. Deploy the API

A step-by-step gif of this process can be found by clicking [here](https://dev-to-uploads.s3.amazonaws.com/i/vaf1768w06kknix8v2v5.gif)

> 🗒️ Given our full API URL: `https://n4hgk2fh3h.execute-api.us-east-1.amazonaws.com/default/custom-resource-demo-function`

> Note that
> - `https://n4hgk2fh3h.execute-api.us-east-1.amazonaws.com` is our base URL
> - `default` is the stage name
> - `custom-resource-demo-function` is the specific endpoint we are hitting

🎉We did it🎉

---

Now comes the simple part where we update our frontend code to use our newly created API!

We'll keep it simple where the data from the API will display on the page when a user clicks a button. Go ahead and update your code with the following, bearing in mind to use your own resource credentials.

```jsx
import React from "react";
import "./App.css";
import Amplify, { API } from "aws-amplify";
import { withAuthenticator, AmplifySignOut } from "@aws-amplify/ui-react";

Amplify.configure({
  //Auth is the same as before
  Auth: {
    region: "us-east-1",
    userPoolId: "us-east-1_T2ZfRqx59",
    userPoolWebClientId: "61l5lp494qnb60ek6h0ohonspp",
  },
  // Add in our new API, "name" can be whatever we want
  API: {
    endpoints: [
      {
        name: "demo",
        endpoint:
          "https://n4hgk2fh3h.execute-api.us-east-1.amazonaws.com/default",
      },
    ],
  },
});

function App() {
  const [apiData, setApiData] = React.useState("");

  const handleClick = async () => {
    const data = await API.get("demo", "/custom-resource-demo-function", {
      headers: {
        //🚨 don't embed api keys in real life!!
        "x-api-key": "grC9ZygLZuaek3hS8Uh6I9rtC5IgYvwd36EAjaba",
      },
    });
    setApiData(data.body);
  };
  return (
    <div className="App">
      <header className="App-header">
       <p>Welcome! My favorite quote is: {apiData}</p>
        <button onClick={handleClick}>Click me!</button>
        <AmplifySignOut />
      </header>
    </div>
  );
}

export default withAuthenticator(App);
```

If you were logged out of your application, go ahead and log back in. Now click the button and bear the fruits of all your hard work!🐻🍒

![application with json displayed on page](https://dev-to-uploads.s3.amazonaws.com/i/jidatutuxgml0pljkv3p.png)

---

It's worth reiterating, that AWS Amplify's selling point is that it does all of this configuration for us, so that we can focus on our specific business logic✨ 

However, I've personally used Amplify on projects where the frontend team was a bit silo'd from the backend team, and we relied on the backend to create our resources as a compromise to us using a lot of Amplify's built-in components and methods.

Hope this helps! Be sure to follow for more content on AWS Amplify!