56

In order to prevent users who have not logged in to call my lambda function through the AWS API Gateway, I'm using the Custom Authorizer lambda solution.

If the request is authorized (200) and I get a response from the called lambda everything works fine and I get the Access-Control-Allow-Origin header.

But if the request is not authorized, I get a 401 that has no Access-Control-Allow-Origin header, therefore preventing me from reading the 401 status of the response and redirecting the user to the log-in page.

I believe this is because the Custom Autorization mechanism is unaware that the request needs to use CORS. Does anyone know that this is actually the issue? Are you aware of any possible solution?

Pat Myron
  • 4,437
  • 2
  • 20
  • 39
hanbzu
  • 880
  • 1
  • 8
  • 14

10 Answers10

27

I'm happy to announce the new Gateway Responses feature which allows you to customize the error responses for requests that don't call your integration. This allows you to ensure that CORS headers are included, even on failed auth requests.

Read more in our documentation, which includes a CORS example.

Dunedan
  • 7,848
  • 6
  • 42
  • 52
Bob Kinney
  • 8,870
  • 1
  • 27
  • 35
  • 2
    Here what you need: http://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-gateway-responses-in-swagger.html?shortFooter=true – Sergei Nov 15 '17 at 21:25
  • 1
    Where do that json snipped need to go? – Federico Fissore Jan 08 '18 at 11:14
  • 2
    In case you're working with serverless.yaml, this is what you need to know https://github.com/serverless/examples/blob/master/aws-node-auth0-custom-authorizers-api/serverless.yml#L33 – Federico Fissore Jan 08 '18 at 11:47
  • how to return cors header from lambda fucntion itself like using callbcack or context.fail – Vignesh Apr 03 '18 at 11:48
  • Be aware, if you have no events.http defined under functions section then you will get a serverless error. `The CloudFormation template is invalid: Template format error: Unresolved resource dependencies [ApiGatewayRestApi] in the Resources block of the template` – user1653042 Jul 07 '21 at 20:07
  • Only for REST APIs... – Matthias Jun 06 '23 at 20:09
20

Yes, this is a known bug with API Gateway custom authorizers. Thanks for bringing this to our attention. The team will update this post when we've deployed a fix. Apologies for the inconvenience.

Lorenzo d
  • 2,016
  • 10
  • 17
10

This works for me (inline in AWS::APIGateway: definition)

Resources:
  MyApi:
    Type: AWS::Serverless::Api
    Properties:
      StageName: Dev
      GatewayResponses:
        UNAUTHORIZED:
          StatusCode: 401
          ResponseParameters:
            Headers:
              Access-Control-Allow-Origin: "'*'"
        ACCESS_DENIED:
          StatusCode: 403
          ResponseParameters:
            Headers:
              Access-Control-Allow-Origin: "'*'"
        DEFAULT_5XX:
          StatusCode: 500
          ResponseParameters:
            Headers:
              Access-Control-Allow-Origin: "'*'"
        RESOURCE_NOT_FOUND:
          StatusCode: 404
          ResponseParameters:
            Headers:
              Access-Control-Allow-Origin: "'*'"  

The available GatewayResponses Naming are:

DEFAULT_INTERNAL
DEFAULT_4XX
DEFAULT_5XX
RESOURCE_NOT_FOUND
UNAUTHORIZED
ACCESS_DENIED
AUTHORIZER_FAILURE
AUTHORIZER_CONFIGURATION_ERROR
MISSING_AUTHENTICATION_TOKEN
INVALID_SIGNATURE
EXPIRED_TOKEN
INTEGRATION_FAILURE
INTEGRATION_TIMEOUT
API_CONFIGURATION_ERROR
UNSUPPORTED_MEDIA_TYPE
REQUEST_TOO_LARGE
BAD_REQUEST_PARAMETERS
BAD_REQUEST_BODY
THROTTLED
QUOTA_EXCEEDED
INVALID_API_KEY
WAF_FILTERED

So you could specify the Response customization for these Controlled AWS responses.

Murat Çorlu
  • 8,207
  • 5
  • 53
  • 78
danipenaperez
  • 583
  • 5
  • 12
9

The easiest way to resolve this for all 4XX error (including 401 errors) is to go to "Gateway Responses" and then select "Default 4XX" and then add the the header "Access-Control-Allow-Origin" with the value '*'.

See screenshot:

Cristik
  • 30,989
  • 25
  • 91
  • 127
Alex Robinson
  • 91
  • 1
  • 1
  • We are not able to use wildcard ('*') because we need to send a cooke. Do you know of a way to allow more than one Access-Control-Allow-Origin without using the wildcard? – Seth McClaine Jul 19 '18 at 16:54
5

Because it took me a while to figure out how to put it all together in Cloud Formation, here is a snippet showing how to set it up.

...
    MyApi:
      Type: "AWS::ApiGateway::MyApi"
      Properties:
        Description: My API
        Name: "my-api"
    MyApiAuthorizer:
      Type: "AWS::ApiGateway::Authorizer"
      Properties:
         Name: "my-api-authorizer"
         IdentitySource: "method.request.header.Authorization"
         ProviderARNs:
           - !GetAtt MyUserPool.Arn
         RestApiId: !Ref MyAApi
         Type: COGNITO_USER_POOLS
    MyApiGatewayResponse:
      Type: "AWS::ApiGateway::GatewayResponse"
      Properties:
        ResponseParameters:
          "gatewayresponse.header.Access-Control-Allow-Origin": "'*'"
          "gatewayresponse.header.Access-Control-Allow-Headers": "'*'"
        ResponseType: UNAUTHORIZED
        RestApiId: !Ref MyApi
        StatusCode: "401"
pisomojado
  • 893
  • 1
  • 10
  • 11
  • You point me in the rigth way, but after some changes, i put it on MyAPI.GateawyResponses and works: Resources: MyApi: Type: AWS::Serverless::Api Properties: StageName: Dev GatewayResponses: UNAUTHORIZED: StatusCode: 401 ResponseParameters: Headers: Access-Control-Allow-Origin: "'*'" – danipenaperez Sep 23 '19 at 09:32
4

If, like me, you are running into problems with API Gateway V2, specifically with an HTTP API - the ANY method doesn't seem to work with the plug and play CORS offering. I had to individually create a route for each method (annoying because they all call the same lambda function, but oh well).

Conrad S
  • 155
  • 1
  • 7
0

Adding to the answers above, if you're not using Cloudformation/SAM template, you can save yourself some manual steps using this python script:

import boto3
import sys

if len(sys.argv) != 3:
    print("usage: python script.py <API_ID> <STAGE>")
    exit()

client = boto3.client('apigateway')

response = client.put_gateway_response(
    restApiId=sys.argv[1],
    responseType='UNAUTHORIZED',
    statusCode='401',
    responseParameters={
        "gatewayresponse.header.Access-Control-Allow-Origin": "'*'",
        "gatewayresponse.header.Access-Control-Allow-Headers": "'*'"
    }
)
response = client.create_deployment(
    restApiId=sys.argv[1],
    stageName=sys.argv[2])
amsh
  • 3,097
  • 2
  • 12
  • 26
0

To fix this for our SPA (we are using AWS Cognito authorizer) we added the next Response Headers in DEFAULT 4xxx and DEFAULT 5xxx Gateway responses:

Access-Control-Allow-Origin '{url_of_your_front-end}'
Access-Control-Allow-Headers '{url_of_your_front-end}'

we set {url_of_your_front-end} instead of '*', because the browser didn't like it :D

as an additional we set Access-Control-Allow-Credentials 'true' header to make a browser happy.

Picture with all headers

  • adding just url_of_your_front-end does not work for us because we have a micro front end app and we have multiple versions of the frontend based on the vendor and all vendor have different URLs – Ngen CMS Feb 06 '22 at 20:55
0

Adding the response header "Access-Control-Allow-Origin" for the Unauthorized response should fix this issue

  • Select the Api
  • Select the Gateway Responses
  • Select Unauthorized
  • Edit the response header and add Access-Control-Allow-Origin with value "*"

enter image description here

Ngen CMS
  • 146
  • 1
  • 6
0

Like the other answers, use the gateway response to resolve. But you must remember that after adding the header, it will not work until the API is deployed.