Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

API Gateway returning 403 - Forbidden

I have an API Gateway with an endpoint that is fulfilled by AWS Lambda proxy integration. I have also configured a custom authorizer for this endpoint. I am seeing an issue where the first request that I make to this endpoint is successful, but additional calls will fail; I get a 403 - Forbidden error. If I wait a while, I can make another request that succeeds but then I start seeing the same problem.

Here's my code for the authorizer:

const jwt = require('jsonwebtoken');

exports.authorizer = async function (event, context) {
  const bearerToken = event.authorizationToken.slice(7);
  const { payload } = jwt.decode(bearerToken);
  return {
    principalId: payload.sub,
    policyDocument: {
      Version: '2012-10-17',
      Statement: [{
        Action: 'execute-api:Invoke',
        Effect: 'Allow',
        Resource: event.methodArn,
      }],
    },
  };
};

In the API Gateway logs for this endpoint I can see that the authorizer is returning Allow but I can still see that the authorization fails:

(50ac5f87-e152-4933-a797-63d84a528f61) The client is not authorized to perform this operation.

Does anyone know how or why this could happen?

like image 992
tefad Avatar asked May 03 '19 11:05

tefad


1 Answers

The problem I think is in the response your authorizer is sending back. In your policy document you can see you are returning Resource: event.methodArn.

This would typically work, provided that your authorizer is not caching the response from your custom authorizer (this is on by default). The problem you're experiencing arises when you make a request API Gateway and get back a cached authorizer response that doesn't match the requested ARN of the current request. This post explains more about how Lambda authorizers work, including caching.

You can verify that this is the problem by going into the AWS console and disabling caching for your custom authorizer; once you do this you should no longer experience this problem.

So how can you fix this long term? There's a couple of options:

Disable caching: This is the simplest solution. The downside is that you're now invoking your authorizer with every request which will introduce more latency into your API.

Return a broader policy: This is the best solution, but more complicated. There's a couple options here, you can return multiple Allow policies in your authorizer response that apply to any endpoint that uses this authorizer.

If you look at the format of an authorizer request you'll see that the methodArn is in the following format:

{
    "type":"TOKEN",
    "authorizationToken":"{caller-supplied-token}",
    "methodArn":"arn:aws:execute-api:{regionId}:{accountId}:{appId}/{stage}/{httpVerb}/[{resource}/[{child-resources}]]"
}

So you're probably returning something like this for the methodArn:

arn:aws:execute-api:us-west-2:123456789012:ymy8tbxw7b/*/GET/my-resource/e56bde3c-7c77-46c6-bdf0-ab4a8cb5f5ca

A broader policy that would apply to any resource for this endpoint would be:

arn:aws:execute-api:us-west-2:123456789012:ymy8tbxw7b/*/GET/my-resource/*

If you have multiple endpoints that use this same authorizer, then you can return multiple policies:

{
  "principalId": "user",
  "policyDocument": {
    "Version": "2012-10-17",
    "Statement": [
      {
        "Action": "execute-api:Invoke",
        "Effect": "Allow",
        "Resource": "arn:aws:execute-api:us-west-2:123456789012:ymy8tbxw7b/*/GET/my-resource/*"
      },
      {
        "Action": "execute-api:Invoke",
        "Effect": "Allow",
        "Resource": "arn:aws:execute-api:us-west-2:123456789012:ymy8tbxw7b/*/POST/my-resource"
      }
    ]
  }
}
like image 119
Daniel Cottone Avatar answered Sep 29 '22 09:09

Daniel Cottone