amplify-cli: API Gateway CORS is blocking localhost access

Before opening, please confirm:

  • I have installed the latest version of the Amplify CLI (see above), and confirmed that the issue still persists.
  • I have searched for duplicate or closed issues.
  • I have read the guide for submitting bug reports.
  • I have done my best to include a minimal, self-contained set of instructions for consistently reproducing the issue.

How did you install the Amplify CLI?

npm i -g @aws-amplify/cli

If applicable, what version of Node.js are you using?

16.6.0

Amplify CLI Version

5.3.0

What operating system are you using?

macOS 11.5.2

Amplify Categories

api

Amplify Commands

add, push, update

Describe the bug

After running amplify update API and adding access restrictions to the API endpoint for only authenticated users, I can no longer access the lambda function from my localhost.

After running this: Screenshot 2021-08-24 at 11 35 10

This happens: Screenshot 2021-08-24 at 11 35 38

Expected behavior

I would expect that place to edit the CORS policies to be obvious or easier to find. I’ve searched the AWS API Gateway console but I found nothing related to CORS policies anywhere.

Reproduction steps

  1. Run amplify ad api and add some lambda function
  2. Refer to the “Describe the bug section” and run that command

GraphQL schema(s)

# Put schemas below this line


Log output

# Put your logs below this line


Additional information

No response

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 47 (14 by maintainers)

Most upvoted comments

Hey y’all 👋 after restricting access to the API route, I was able to add the CORS headers (or uncomment what’s provided from the templates) to mitigate this issue

exports.handler = async (event) => {
  // TODO implement
  const response = {
    statusCode: 200,
    //  Uncomment below to enable CORS requests
+   headers: {
+     'Access-Control-Allow-Origin': '*',
+     'Access-Control-Allow-Headers': '*',
+   },
    body: JSON.stringify('Hello from Lambda!'),
  }
  return response
}

and my sample app.js file in a Next.js app making the call with authentication

// src/pages/_app.js
import { useEffect, useState } from 'react'
import { Amplify, API } from 'aws-amplify'
import { withAuthenticator, AmplifySignOut } from '@aws-amplify/ui-react'
import config from '../aws-exports.js'

Amplify.configure(config)

async function get() {
  const apiName = 'api8019'
  const path = '/hello'
  const myInit = {
    response: true,
  }

  return API.get(apiName, path, myInit)
    .then((response) => {
      console.log('got response', response)
      return response
    })
    .catch((error) => {
      console.log('got error', error)
      return error
    })
}

function App() {
  const [response, setResponse] = useState()
  const [isLoading, setIsLoading] = useState(false)
  async function getResponse() {
    setIsLoading(true)
    setResponse(await get())
    setIsLoading(false)
  }
  useEffect(() => {
    getResponse()
  }, [])
  return (
    <div>
      <AmplifySignOut />
      <button onClick={getResponse}>refresh</button>
      {isLoading && <span>Loading...</span>}
      <code>
        <pre>{JSON.stringify(response, null, 2)}</pre>
      </code>
    </div>
  )
}

export default withAuthenticator(App)

I had the same error and checked the Lambda logs, turns out the lambda was running out of memory. So I increased the memory and timeout and it solved the issue! Also enabling CORS through API Gateway console didn’t solve the issue for some reason, but enabling CORS in the lambda code helped solve the issue :

    return {
        statusCode: 200,
        headers: {
          "Access-Control-Allow-Origin": "*",
          "Access-Control-Allow-Headers": "*"
      }, 
        body: ... ,
    };

Hope this helps anybody struggling with the same issue!

thanks @RoniqueRicketts, I followed your trail and solved my CORS issue, which suddenly appeared after adding groups and cognito in the identity pool

  • sign in, confirm access to REST api
  • sign out
  • create new group on cli, push
  • in the cognito UI, add user to the new group
  • sign in, observe CORS error on same gateway api resource
  • sign out
  • remove user from group in cognito UI
  • sign in, confirm access to api

solved this in the IAM ui

  • sign out
  • add user back into the new group
  • go to Roles, filter for your group, click for detail
  • attach policies: amplify-xxxxxxxxx-xxxxxxxxx-xxxxxxxxx-authRole which was created by amplify, granting access to each route by method and resource
  • sign in, confirm access to the resource

For lack of a better option, I resolved this issue by redeploying my backend infrastructure from scratch.

Here’s what I did:

  1. Made a backup copy of all my files
  2. Deleted the amplify folder and the aws-exports.js file
  3. Deleted the production environment from Amplify’s Admin UI (hosting, auth, API, and functions are gone)
  4. Create a backend environment using AWS Amplify Console (the default environment is called staging and you can’t change that)
  5. Ran amplify pull --appId xxxxxxxx --envName production on the root of my repo to get access to that backend from the CLI
  6. Ran amplify env add production to add a backend environment called production
  7. Ran amplify env remove staging to remove the default backend environment staging (unfortunately one has to do this gymnastic because you can’t change the default environment name)
  8. Ran amplify push to save changes to the cloud
  9. On Amplify’s Admin UI, added auth (I can’t do this using the CLI because it forces a username field on my frontend)
  10. Ran amplify add api allowing access only to authorized users
  11. Ran amplify push to save changes

Now I can only hit my API from logged-in users ✅

My request was getting blocked with a 403 Error code. My requests were getting block at the api gateway level and I was unable to hit my lambda functions associated with the API. The reason behind this failure was that I had added users groups to my Cognito Userpool before running amplify add api . When user groups are added user roles are also added but no policies are associated with said user role. My suggestion would be to update any user Role policy with the permission to access the api-gateway service when we run amplify add api to a project that already has cognito userpools added.

Hey @andrecasal yes I used amplify add api and added a GET and POST route.

  1. add the API with an unsecured route to confirm no CORS issues

     > amplify add api
     ? Please select from one of the below mentioned services: REST
     ? Provide a friendly name for your resource to be used as a label for this category in the project: api8019
     ? Provide a path (e.g., /book/{isbn}): /hello
     ? Choose a Lambda source Create a new Lambda function
     ? Provide an AWS Lambda function name: 8019hello
     ? Choose the runtime that you want to use: NodeJS
     ? Choose the function template that you want to use: Hello World
    
  2. secure the route

     > amplify update api
     ? Please select from one of the below mentioned services: REST
     ? Please select the REST API you would want to update api8019
     ? What would you like to do Update path
     ? Please select the path you would want to edit /hello
     ? Provide a path (e.g., /book/{isbn}): /hello
     ? Choose a Lambda source Use a Lambda function already added in the current Amplify project
     ? Choose the Lambda function to invoke by this path 8019hello
     ? Restrict API access Yes
     ? Who should have access? Authenticated users only
     ? What kind of access do you want for Authenticated users? read
    
  3. modify the Lambda handler to include CORS headers

     exports.handler = async (event) => {
       // TODO implement
       const response = {
         statusCode: 200,
         //  Uncomment below to enable CORS requests
         headers: {
           'Access-Control-Allow-Origin': '*',
           'Access-Control-Allow-Headers': '*',
         },
         body: JSON.stringify('Hello from Lambda!'),
       }
       return response
     }
    
  4. add a POST route

     > amplify update api
     ? Please select from one of the below mentioned services: REST
     ? Please select the REST API you would want to update api8019
     ? What would you like to do Add another path
     ? Provide a path (e.g., /book/{isbn}): /hello-post
     ? Choose a Lambda source Create a new Lambda function
     ? Provide an AWS Lambda function name: 8019hellopost
     ? Choose the runtime that you want to use: NodeJS
     ? Choose the function template that you want to use: Hello World
     ...
     ? Restrict API access Yes
     ? Who should have access? Authenticated users only
     ? What kind of access do you want for Authenticated users? create
    
  5. modify the Lambda handler to respond to POST requests and include CORS headers

     exports.handler = async (event) => {
       // TODO implement
       const { name } = JSON.parse(event.body)
       const body = JSON.stringify(`Hello, ${name}!`)
       const response = {
         statusCode: 200,
         //  Uncomment below to enable CORS requests
         headers: {
           'Access-Control-Allow-Origin': '*',
           'Access-Control-Allow-Headers': '*',
         },
         body,
       }
       return response
     }
    
  6. run amplify push

  7. in my frontend Next.js _app.js file:

    code
    import { useEffect, useState } from 'react'
    import { Amplify, API, Auth } from 'aws-amplify'
    import { withAuthenticator, AmplifySignOut } from '@aws-amplify/ui-react'
    import config from '../aws-exports.js'
    
    Amplify.configure(config)
    
    async function get() {
      const apiName = 'api8019'
      const path = '/hello'
      const myInit = {
        // response: true,
      }
    
      return API.get(apiName, path, myInit)
        .then((response) => {
          console.log('got response', response)
          return response
        })
        .catch((error) => {
          console.log('got error', error)
          return error
        })
    }
    
    async function post() {
      const apiName = 'api8019'
      const path = '/hello-post'
      const myInit = {
        // response: true,
        body: { name: 'World' },
      }
    
      return API.post(apiName, path, myInit)
        .then((response) => {
          console.log('got response', response)
          return response
        })
        .catch((error) => {
          console.log('got error', error)
          return error
        })
    }
    
    function App() {
      const [response, setResponse] = useState()
      const [isLoading, setIsLoading] = useState(false)
      async function getResponse() {
        setIsLoading(true)
        const getResponseData = await get()
        setResponse((existing) => ({ ...existing, get: getResponseData }))
        setIsLoading(false)
      }
      async function postResponse() {
        setIsLoading(true)
        const postResponseData = await post()
        setResponse((existing) => ({ ...existing, post: postResponseData }))
        setIsLoading(false)
      }
      function refresh() {
        getResponse()
        postResponse()
      }
      useEffect(() => {
        refresh()
      }, [])
      return (
        <div>
          <AmplifySignOut />
          <button onClick={refresh}>refresh</button>
          {isLoading && <span>Loading...</span>}
          <code>
            <pre>{JSON.stringify(response, null, 2)}</pre>
          </code>
        </div>
      )
    }
    
    export default withAuthenticator(App)
    
  8. run the Next.js development server with yarn next and authenticate

    image

What is the status code you’re receiving on the failed POST call?

@RoniqueRicketts thanks for the offer to help. I haven’t created groups to my cognito, so it would be a surprise I would require an update to any groups.

My problem was solved literally by removing body:{} from the API call options. This is a call which works against GET paths created with earlier amplify-cli version:

const APIName = 'apiname';
const URL = '/endpoint';
const options = { 
	headers: { "Content-Type": "application/json" }, 
	body: {},
	response: true 
};
const response = await API.get(APIName, URL, options);

And this is an API call which works post-4.50.2 amplify-cli created API paths:

const APIName = 'apiname';
const URL = '/endpoint';
const options = { 
	headers: { "Content-Type": "application/json" }, 
	response: true 
};
const response = await API.get(APIName, URL, options);

Using body as part of the GET call obviously is not according to the HTTP definition, but I just happend to use it to move some telemetry data from the app to the backend… Headers themselves have a limit (4-8Kb) depending on webserver of choice and its config) how much data you can transfer and for certain use cases the body is better place to transfer big telemetry payloads.

@pepso as per the information sent by @jasonhargrove above you’ll have to update the API to the groups you make. I surely believe the documentation should update on every update with beginner friendly content. That could minimize a lot of issue that I’m seeing on here. Right now there is also an issue with the API getting custom_headers but the documentation suggests how to send them but the information is incorrect as it doesn’t match the actual reaction of the API on API Gateway.

I fixed my issue and identified a breaking change in the way how amplify handles api calls/paths between versions:

Background: In my web-app, every API call to my endpoints contain couple of variables in the body of the call (where I transfer some metrics about the user experience). In my backend I parse them with middleware using app.use(metricParsers()).

I found out that there is a change API paths created pre-4.50.2, GET calls with “body” go through API Gatway API paths created post-4.50.2, GET calls fail as CORS error if body is defined as part of the paylod

Basically I conclude that API Gateway configuration have become more strict with API updates in past couple of months.

@josefaidt I strongly suggest this is not a documentation issue, but real technical problem with amplify-cli. Do I create a new issue, or can you reopen this and triage further as per my below details?

  1. I have an API originally created with amplify-cli version ~4.20 with 10 or so paths all working fine and dandy
  2. I was able to upgrade amplify-cli up to 4.52.0 version with manual workaround of parameters.json file clean up, and also getting the auth/unAuth roles recreated as per 4.50.2 minor version upgrade (which fixes api migration to new style of configuring the api gateway)
  3. I created a new api path, a standard lambda as per my project (same “template” as other end points) and I got CORS all over. APIGateway does not invoke the lambda function associated with the newly created path
  4. I decided to create a full “new” lambda example as per the amplify-cli walkthrough like this: image
  5. Deployment all ok, but when I try to GET the end point (from remote server/localhost, or from deployed cloud stack [dev]) I get into CORS trouble.
  6. OPTIONS returns 200, but GET fails: image

Please keep in mind every other API path in the same API Gateway end point continue to work, and be invoked as expected (created and deployed pre-4.50 amplify-cli). This impacts only the new paths created since 4.50.2 upgrade (which was supposed to fix the API migration issues).

I don’t believe this to be documentation only issue, and it got to involve at least newly created out of box api path into an existing api + lambda walkthroughs update so the codegen works as expected.

Please advise what needs to be done to fix the disconnect between amplicy-cli created templates to working API path to Lambda invokes without CORS.

not sure but did not seem to matter which order I added auth or api as reported, so I dug a little further

tldr; after you add a group, you can use CLI to update your API, and attach the policy for that group

just now solved this with the cli, which is probably the intended flow

  • after adding groups

  • amplify api update choose REST

  • select the api and path and follow the flow until you get to restrict API access

  • select restrict access by both (auth and group)

  • select auth users only and method

  • select group(s)

  • it will then ask you to confirm the method for each group

  • amplify push

  • confirm it added the policy to the group(s) in the flow via IAM ui

  • ensure test user is in an impacted group

  • sign out

  • sign in, confirm access to resource

Error 403

I think @RoniqueRicketts and me have the exact same problem.

I have the same issue I am on the same version on Amplify (the same issue was on the previous version also). I am on Windows 10 OS. What I found out for this issue so far:

  1. The request never reaches the Lambda functions connected to the API.
  2. If I turn off Authenticated users only I am able to hit the open API with the same failed frontend request from before and get a 200 response.
  3. Disabling the IAM Feature off the endpoint makes the API accessible which gives me 200 response but the API is not protected.
  4. If I update the policy that controls the authRole to full access I get the same error.