3

I'm trying to implement custom authorization on API Gateway, that would check user's permissions on each particular endpoint behind it, by reading them from the DynamoDB.

I associated the authorizer with the method in question (screenshot below) enter image description here

The authorizer seems to be working ok, and it returns policy that looks fine to me (have a look underneath)

{
    "policyDocument" : {
        "Version" : "2012-10-17",
        "Statement" : [
            {
                "Action" : "execute-api:Invoke",
                "Effect" : "Deny",
                "Resource" : "arn:aws:execute-api:us-east-2:111111111111:mkvhd2q179/*/GET/api/Test"
            }
        ]
    },
    "principalId"    : "*"
}

However, regardless of the Effect authorizer returned inside the policy document, API Gateway still let's all requests pass. I get the status 200 as well as the result set from the API endpoint underneath.

Any ideas as to why the API Gateway would ignore the policy?

P.S. I tried with the explicit principalID (the username/subject from the token) prior to putting an asterisk there. It behaved the same.

P.P.S Here's completely dummed down version of my Lambda function, currently set up to allways return Deny as policy Effect...

public class Function
{
    public AuthPolicy FunctionHandler(TokenAuthorizerContext request, ILambdaContext context)
    {
        var token = request.AuthorizationToken;

        var stream = token;
        var handler = new JwtSecurityTokenHandler();
        var jsonToken = handler.ReadToken(stream);
        var tokenS = handler.ReadToken(token) as JwtSecurityToken;

        return generatePolicy(tokenS.Subject, "Deny", "arn:aws:execute-api:us-east-2:111111111111:mkvhd2q179/*");
    }

    private AuthPolicy generatePolicy(string principalId, string effect, string resource)
    {

        AuthPolicy authResponse = new AuthPolicy();
        authResponse.policyDocument = new PolicyDocument();
        authResponse.policyDocument.Version = "2012-10-17";// default version
        authResponse.policyDocument.Statement = new Statement[1];
        authResponse.principalId = "*";

        Statement statementOne = new Statement();
        statementOne.Action = "execute-api:Invoke"; // default action
        statementOne.Effect = effect;
        statementOne.Resource = resource;

        authResponse.policyDocument.Statement[0] = statementOne;

        return authResponse;
    }
}

public class TokenAuthorizerContext
{
    public string Type { get; set; }
    public string AuthorizationToken { get; set; }
    public string MethodArn { get; set; }
}

public class AuthPolicy
{
    public PolicyDocument policyDocument { get; set; }
    public string principalId { get; set; }
}

public class PolicyDocument
{
    public string Version { get; set; }
    public Statement[] Statement { get; set; }
}

public class Statement
{
    public string Action { get; set; }
    public string Effect { get; set; }
    public string Resource { get; set; }
}
Eedoh
  • 5,818
  • 9
  • 38
  • 62
  • 1
    Have you tried disabling the caching for authorizer ? The policy and token is cached for the TTL you mentioned in Authorizer. – Sangam Belose Apr 17 '19 at 13:08
  • I just did, after seeing your comment (thanks for the useful tip). I completely disabled caching on the Authorizer Lambda, and shortened TTL on the integration request setup page to the minimal value allowed of 50ms. It did not help. Still ignoring the authorizer. – Eedoh Apr 17 '19 at 14:25
  • 1
    For debug purposes, can you try with a broader arn? Like `arn:aws:execute-api:us-east-2:111111111111:mkvhd2q179/*` – jogold Apr 17 '19 at 20:04
  • 2
    Can you confirm that you associated your Custom Authorizer with a resource in its Method Request? and that you deployed your API after this? – jogold Apr 17 '19 at 20:12
  • @jogold Yes (I just added the scrshot to the original question). I did not re-deploy the API after that originally, but I have now, and there's no difference still. – Eedoh Apr 18 '19 at 08:33
  • I also tried broader arn, no change. I know it's something stupid, but I can't seem to find the cause for it. – Eedoh Apr 18 '19 at 10:38
  • Can you share the code of your Lambda function? – jogold Apr 18 '19 at 10:40
  • @jogold I just did. I dumbed it down to always return Deny, just so I can the API GW react to the policy... – Eedoh Apr 18 '19 at 13:01
  • 1
    Can you log the request and the generated policy document in your code so that you can confirm that when calling your API your Lambda gets called (by checking that you see logs in CloudWatch)? – jogold Apr 18 '19 at 13:50
  • @jogold Jus did that. Looks to me as if the authorizier does not get fired at all. It only logs output when testing the authorizer lambda itself (through the AWS console). When I try hitting the API method endpoint from the outside, it does not get fired. Neither did it fire when testing the method on API GW Console. – Eedoh Apr 18 '19 at 15:54
  • It means something is misconfigured in either API Gateway or Lambda, please follow this step by step https://docs.aws.amazon.com/apigateway/latest/developerguide/configure-api-gateway-lambda-authorization-with-console.html – jogold Apr 19 '19 at 07:17
  • Did you ever solve this? – Kenny Worden Aug 24 '20 at 21:31

4 Answers4

2

TL;DR; Remove/change/check the "Resource Policy" set in the Gateway.

I had a similar problem. Somehow I had a "allow * principal access to * resources" policy set in the Resource Policy on the Gateway which was being combined with whatever the Authorizer was returning. I ended up removing all resource policies and let the Authorizer decide.

radu-c
  • 174
  • 6
2

I had this problem as well. Turns out that making the request from the API Gateway console screen (e.g. https://us-west-2.console.aws.amazon.com/apigateway/) doesn't appropriately invoke the authorizer.

I'm guessing its because your console session has its own IAM policy, which interferes with the authorizer policy.

The solution was to manually CURL the endpoint outside of the API Gateway console.

Additionally, do not forget to deploy your API after you make your changes! Otherwise your changes won't be taking effect:

enter image description here

Kenny Worden
  • 4,335
  • 11
  • 35
  • 62
0

I had a similar issue. The way our API gateway resource policy was set up to allow us to execute any API in the account level (arn:aws:execute-api:us-east-1:xxxxx:*).

Even though implemented fine-grained access where we return a policy to allow only particular arn the API gateway resource policy was taking precedence. So I have removed the resource policy and redeployed the API and it was allowing that particular API and denying the others. OR u can try vice versa based on how you configure your Effect and policy statement.

Initial Resource Policy:(I removed and redeployed)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": "*",
            "Action": "execute-api:Invoke",
            "Resource": "arn:aws:execute-api:us-east-1:xxxxx:*",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": [
                        "xx.xx.xx.xxx/24"
                    ]
                }
            }
        }
    ]
}

Final Lambda Auth Policy returned:

 {
    "principalId": "xxxxxxxxxx",
    "policyDocument": {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "execute-api:Invoke"
                ],
                "Resource": [
                    "arn:aws:execute-api:us-east-1:xxxxx:bxxxx/*/POST/*/someresource"
                ]
            }
        ]
    }
}
0

The AWS Documentation is confusing... it seems that you still need to use the "callback" to do the trick and is not enough to return an "Deny" policy...

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

  if (invalidToken) {
    callback("Unauthorized", null);
  }

  // create a valid policy

  return validPolicy
}
Ari Waisberg
  • 1,186
  • 1
  • 12
  • 23