AWS API Gateway with Custom Authorizer and CORS Intermittent 200 then 403 then 200 … Strange

回眸只為那壹抹淺笑 提交于 2021-02-16 14:48:11

问题


I have an 1 Amazon Api Gateway setup with a custom authorizer (the authorizer basically just returns allow for anything)

I enabled CORS, and this is running from jQuery webpage.

I have two method

  1. /vehicles (returns a list of car)
  2. /bookings (returns booking details)

The behavior I am seeing, is the first request goes fine, I see it pull the OPTIONS, then perform a GET request. Then, I hit the other method the OPTIONS works, then the get returns a 403, but if I launch the request again (On the same resource), I get a 200

I'm using Cloudformation, but I noticed the same behaviour when I was using the Serverless Framework.

Below are some screen shots for my sanity and hopefully someone else has seen this strangeness.

Below is a portion of my Cloudformation YAML template, I'm learning this as I do it.

 HelloAPI:
    Type: AWS::Serverless::Api
    Properties:
      StageName: !Sub ${Environment}
      DefinitionBody:
        swagger: 2.0
        info:
          title:
            Ref: AWS::StackName
        securityDefinitions:
          test-authorizer:
            type: apiKey
            name: Authorization
            in: header
            x-amazon-apigateway-authtype: custom
            x-amazon-apigateway-authorizer:
              type: token
              authorizerUri:
                Fn::Sub: arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${AuthorizerFunc.Arn}/invocations
              authorizerResultTtlInSeconds: 5
        paths:
          /vehicles:
            get:
              x-amazon-apigateway-integration:
                httpMethod: POST
                type: aws_proxy
                uri:
                  !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${VehiclesLambda.Arn}/invocations
              responses: {}
              security:
                - test-authorizer: []
            options:
              tags:
              - "CORS"
              summary: "CORS support"
              description: "Enable CORS by returning correct headers\n"
              consumes:
              - "application/json"
              produces:
              - "application/json"
              parameters: []
              responses:
                "200":
                  description: "Default response for CORS method"
                  headers:
                    Access-Control-Allow-Headers:
                      type: "string"
                    Access-Control-Allow-Methods:
                      type: "string"
                    Access-Control-Allow-Origin:
                      type: "string"
              x-amazon-apigateway-integration:
                type: "mock"
                requestTemplates:
                  application/json: "{\n  \"statusCode\" : 200\n}\n"
                responses:
                  default:
                    statusCode: "200"
                    responseParameters:
                      method.response.header.Access-Control-Allow-Headers: "'X-Amz-Date,Authorization,X-Api-Key'"
                      method.response.header.Access-Control-Allow-Methods: "'*'"
                      method.response.header.Access-Control-Allow-Origin: "'*'"
                    responseTemplates:
                      application/json: "{}\n"
          /bookings:
            get:
              x-amazon-apigateway-integration:
                httpMethod: POST
                type: aws_proxy
                uri:
                  !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${BookingsLambda.Arn}/invocations
              responses: {}
              security:
                - test-authorizer: []
            options:
              tags:
              - "CORS"
              summary: "CORS support"
              description: "Enable CORS by returning correct headers\n"
              consumes:
              - "application/json"
              produces:
              - "application/json"
              parameters: []
              responses:
                "200":
                  description: "Default response for CORS method"
                  headers:
                    Access-Control-Allow-Headers:
                      type: "string"
                    Access-Control-Allow-Methods:
                      type: "string"
                    Access-Control-Allow-Origin:
                      type: "string"
              x-amazon-apigateway-integration:
                type: "mock"
                requestTemplates:
                  application/json: "{\n  \"statusCode\" : 200\n}\n"
                responses:
                  default:
                    statusCode: "200"
                    responseParameters:
                      method.response.header.Access-Control-Allow-Headers: "'X-Amz-Date,Authorization,X-Api-Key'"
                      method.response.header.Access-Control-Allow-Methods: "'*'"
                      method.response.header.Access-Control-Allow-Origin: "'*'"
                    responseTemplates:
                      application/json: "{}\n"

This is my Anything Goes Authorizer:

'use strict';

const generatePolicy = function(principalId, effect, resource) {
    const authResponse = {};
    authResponse.principalId = principalId;
    if (effect && resource) {
        const policyDocument = {};
        policyDocument.Version = '2012-10-17';
        policyDocument.Statement = [];
        const statementOne = {};
        statementOne.Action = 'execute-api:Invoke';
        statementOne.Effect = effect;
        statementOne.Resource = resource;
        policyDocument.Statement[0] = statementOne;
        authResponse.policyDocument = policyDocument;
    }
    return authResponse;
};

exports.handler = (event, context, callback) => {

    console.log("Hit Authorizer")
    console.log(event)


    callback(null, generatePolicy('user123', 'Allow', event.methodArn));

}; 

Anyone else seen this, or know how to debug it ?

I put this on a test site, just it some one wants to see what I am seeing.

https://s3.amazonaws.com/stackoverflowisgreat2/index.html


回答1:


In the custom authorizer code, at the line

statementOne.Resource = resource;

change your resources to this format "arn:aws:execute-api:us-west-2:123456789012:ymy8tbxw7b/*/GET/".

In your case to allow all that would be:

statementOne.Resource = arn:aws:execute-api:us-west-2:123456789012:ymy8tbxw7b/*/*/

This is how AWS understands your authorizer. Because in custom authorizer you can get information from the request header like user, group, etc and then validate the info against your authorization database and decide who or what is allowed to continue the request type POST/GET/OPTION, but API gateway won't know your decision until you provide it with a valid answer in AWS format

{
  "principalId": "yyyyyyyy", // The principal user identification associated with the token sent by the client.
  "policyDocument": {
    "Version": "2012-10-17",
    "Statement": [
      {
        "Action": "execute-api:Invoke",
        "Effect": "Allow|Deny",
        "Resource": "arn:aws:execute-api:{regionId}:{accountId}:{appId}/{stage}/{httpVerb}/[{resource}/[child-resources]]"
      }
    ]
  },
  "context": {
    "stringKey": "value",
    "numberKey": "1",
    "booleanKey": "true"
  },
  "usageIdentifierKey": "{api-key}"  # Optional
}

You can visit this page to understand more about it:

https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-lambda-authorizer-output.html




回答2:


Just talked with an AWS rep about a similar issue. The issue at hand is lambda authorizer caching, which is different than API Gateway caching.

You likely have your lambda authorizer caching by default (see photo), so when you initially make a request, your policy (which is specific to a singular resource), gets cached for the TTL. Subsequent requests to DIFFERENT RESOURCES which use the SAME LAMBDA AUTHORIZER will return the SAME POLICY for your original resource, which is not the resource at hand, and as a result, you get a 403.

Changing the returned policy to something more general a la @Dominic Nguyen's answer is one solution (usually involves adding /*s), but you can also do what I did and just disable caching on your lambda Authorizer:

Then, REMEMBER to REDEPLOY!!! Rep told me to wait 30 seconds after that, then test.



来源:https://stackoverflow.com/questions/51549637/aws-api-gateway-with-custom-authorizer-and-cors-intermittent-200-then-403-then-2

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!