DEV Community

Cover image for Play with CORS using AWS Cognito, API Gateway
Phan Công Thắng
Phan Công Thắng

Posted on • Updated on • Originally published at thangphan.xyz

Play with CORS using AWS Cognito, API Gateway

I'm standing in domain A, I'd like to call an API that was hosted in domain B. Normally, I can't do it without setting CORS for the API. In this post, I'm going to do it using API Gateway and AWS Cognito.

Basic concept

API Gateway

I assume I already have had many resources(API), but I didn't set up the access permission yet. In this case, API Gateway is so helpful, I can config the middleware for whatever API I would like to control.

Apart from authorizer, API Gateway also helps us for controlling the resources(API), connecting with other AWS services.

The reason I use API Gateway is that It's so easy to config middleware between API Gateway and AWS Cognito.

AWS Cognito

AWS Cognito is a service that helps us for building authentication. We can sign up user, sign in, logout, etc...

If I sign in successfully, I can get 3 types of tokens.

  1. access token: the token of my account.
  2. id token: use to access other AWS services. I will use it to verify my API Gateway.
  3. refresh token: use to re-new session.

Lambda function

In this demo, I'm going to use lambda to fake responses in API. You can imagine it's a function that we can make the response for our API.

Implementation

I will have 2 patterns with CORS.

  1. Access the outside API without credentials.
  2. Access the outside API by sending credentials from the client side.

This is my logic.

Without credentials

Without credentials

Credentials

Credentials

Setup

Nextjs

Create a new project:

npx create-next-app --typescript
Enter fullscreen mode Exit fullscreen mode

I give it the name dev-cors.

AWS

I assume you already had an IAM account in order to use AWS services.

Lambda function

I will access to AWS Lambda console environment, and create a new function dev-cors-function.

Dev Cors Function

Edit the response in order to make it looks like a realistic API:

const response = {
  statusCode: 200,
  body: {
    userId: 1,
    id: 1,
    title: "delectus aut autem",
    completed: false
  },
};
Enter fullscreen mode Exit fullscreen mode

And Hit Deploy in order to finish our set up.

AWS Cognito

I'm going to the AWS Cognito console environment, and create new user pool with name dev-cors-cognito.

Dev Cors Cognito

Hit Add app client... I need to create app client in order to make request sign up, sign in, etc...

Give it a name dev-cors-app-client.
The important thing here is that I need to uncheck Generate client secret, if I don't do this, I'm not able to make a request successful(it was an error of AWS Cognito).

Hit Create pool and I finished creating a user pool for our app.

API Gateway

I'm going to API Gateway console, and create new API for our demo.

API Gateway Console

Hit button Build in the REST API column.

And give it a name like dev-cors-api, then click Create API.

Now I have had an API, let's make two resources for our app.

  1. without-credentials
  2. credentials

API Gateway

Note: we need to check button Enable API Gateway CORS in order to make use our API will be enabled CORS.

In without-credentials, let's create a method GET, and connect to the lambda function that I just created in the previous step.

GET Without Credentials

In order to make a request successful from the client-side, I need to config Access-Control-Allow-Origin in my API.

In GET method, hit Integration Response -> Method Response.

Integration Response

Hit Add Header, and add Access-Control-Allow-Origin.

Now, I go back to Integration Response, I'm able to see
Access-Control-Allow-Origin. But I have to change the value to '*'.

Access Control Allow Origin

With credentials I do the same way with without-credentials, create a method GET, but I will create an authorizer for the resource.

Hit Authorizers tab, and select the user pool that I just created in the previous step.

Dev Cors Authorizer

Give it the name dev-cors-authorizer, and fill out Authorization in Token Source * input. I will need to send Authorization: token from the client in order to verify.

Ok cool! Now, I come back to credentials resource, and hit GET -> Method Request and select dev-cors-authorizer for Authorization. If it wasn't showed, you maybe need to reload your page.

Authorization Setup

Finally, I need to deploy my API. Let's do it:
Deploy API Gateway

Here is my API:
API production

Coding

Let's create a file .env, and set up a variable point to our API Gateway:

NEXT_PUBLIC_API_GATEWAY='https://i83ybr0e1d.execute-api.us-west-2.amazonaws.com/dev'
Enter fullscreen mode Exit fullscreen mode

Without Credentials

Let's create a page in nextjs without-credentials.tsx

import * as React from 'react'

function WithoutCredentials() {
  React.useEffect(() => {
    async function fetchData() {
      const data = await fetch(
        `${process.env.NEXT_PUBLIC_API_GATEWAY}/without-credentials`,
      )
      const resJson = await data.json()

      // do some stuff in here.
    }

    fetchData()
  }, [])
  return (
    <div>
      <h1>Without Credentials</h1>
    </div>
  )
}

export default WithoutCredentials

Enter fullscreen mode Exit fullscreen mode

Hit Network tab and take a glance at the response:

Request Successful Without Credentials

You can try to remove Access-Control-Allow-Origin in API Gateway, and you will see the CORS error.

CORS Error

Credentials

This is the way that I'm going to do:

  1. Sign up an account to AWS Cognito.
  2. Sign in with the account from step one.
  3. Get id token after signed in.
  4. Use id token to call to API Gateway.

Let's do it step by step!

First of all, I need to install amplify in order to interact with AWS Cognito:

npm install aws-amplify
Enter fullscreen mode Exit fullscreen mode

In order to connect to AWS Cognito, I also need to config some property in amplify:

Amplify.configure({
  Auth: {
    region: 'us-west-2.',
    userPoolId: 'us-west-2_ltdxXTVoV',
    userPoolWebClientId: '7n2162uuqkck3jpa4cjv7fnh69',
  },
})
Enter fullscreen mode Exit fullscreen mode

And the code for signing up an account to AWS Cognito:

const username = 'Username'
const password = 'Password@1

async function signUp() {
    try {
      const {user} = await Auth.signUp({
        username,
        password,
        attributes: {
          email: 'email@gmail.com',
        },
      })
      console.log(user)
    } catch (error) {
      console.log('error signing up:', error)
    }
  }
}

<button onClick={signUp}>Sign Up</button>
Enter fullscreen mode Exit fullscreen mode

After signing up, I have to sign in with the username, password that I just signed up for. But before I do this, I need to go to the AWS Cognito in order to confirm the user.

  const [idToken, setIdToken] = React.useState('')
  async function signIn() {
    try {
      const user = await Auth.signIn(username, password)

      console.log('user', user)
      setIdToken(user.signInUserSession.idToken.jwtToken)
    } catch (error) {
      console.log('error signing in', error)
    }
  }
Enter fullscreen mode Exit fullscreen mode

Now, I have had the idToken that allows me access to API Gateway's resources.

Let's try a request without idToken:

  async function callAPIGateway() {
    try {
      const data = await fetch(
        `${process.env.NEXT_PUBLIC_API_GATEWAY}/credentials`
      )
    } catch (error) {
      console.log('error calling API gateway', error)
    }
  }
Enter fullscreen mode Exit fullscreen mode

I got an CORS error:

CORS Error Example

Let's try to add the idToken I have just received from signing in.

  async function callAPIGateway() {
    try {
      const data = await fetch(
        `${process.env.NEXT_PUBLIC_API_GATEWAY}/credentials`,
        {
          headers: {
            Authorization: idToken,
          },
        },
      )
    } catch (error) {
      console.log('error calling API gateway', error)
    }
  }
Enter fullscreen mode Exit fullscreen mode

No! It doesn't work! Hmm, When I send a request to API that is a different domain and with credentials(Authorization), I have to specific
Access-Control-Allow-Origin in the API. It means I have to change '*' -> 'http://localhost:3000'

Let's change it in API Gateway console.
LocalHost Origin

Then re-call the API Gateway.

Successful Calling

It worked!

Conclusion

We are done with calling API from other domains and have a basic overview about CORS, API Gateway, AWS Cognito.
I hope it was helpful for you. Please feel free to refer to the source code.

Discussion (0)