12

I have a lambda authorizer that is written in Python.

I know that with the following access policy I can return 200/403 :

{
        "principalId": "yyyyyyyy",
        "policyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Action": "execute-api:Invoke",
                    "Effect": "Deny",
                    "Resource": "*"
                }
            ]
        },
        "context": {
            "stringKey": "value",
            "numberKey": "1",
            "booleanKey": "true"
        },
        "usageIdentifierKey": "{api-key}"
    }

I'm trying to return 401 error if the customer didn't send any token, therefore I'm raising an exception :

raise Exception("Unauthorized")

The problem with this solution is that the AWS lambda fails and then the execution is marked as a failed execution and not as a successful execution of the lambda. Is there any way to return 401 without failing the lambda ?

Also tried the following like in lambda integration but didn't work:

return  {"statusCode": 401, "body" : "Unauthorized"}
halfer
  • 19,824
  • 17
  • 99
  • 186
JeyJ
  • 3,582
  • 4
  • 35
  • 83
  • 1
    Raising that exception is the only way to return a 401 from an authorizer. You can't customise the status code / response body like you can in a lambda proxy integration. – Phydeaux Nov 19 '20 at 11:45
  • @Phydeaux We are independently experiencing the same problem as the OP. And your comment seems to miss the real issue in this question. Both the OP and we tried the raise exception method. The reason the OP is asking this question I think is because the raise exception method is not wordking.Making a lambda authorizer using python with the code `raise Exception("Unauthorized")` results in a failed execution of the lambda function (i.e. the exception is not caught by the code invoking the lambda handler). Additionally the response from a call to the api gateway is a 500 internal server error. – Maarten Derickx Dec 07 '20 at 11:38
  • So to me it seems that there is something wrong with the api gateway/lambda handler code in AWS that is not working as documented. Because I would hope that raising the unauthorized exception would result in a 401 unauthorized and not a 500 internal server error (as is de default response when an error is thrown from a lambda function that is not caught). – Maarten Derickx Dec 07 '20 at 11:41
  • p.s. I would like to modify the question to provide more usefull info. But sadly enough the suggested edit queue is full, and I do not have enough rep to start reviewing edits. – Maarten Derickx Dec 07 '20 at 11:45
  • 1
    @M.D. feel free to add here details, I will edit the post – JeyJ Dec 07 '20 at 12:23
  • @JeyJ Thanks, before I am going to add more details, I am wondering wether we are really experiencing the same issue? Do you want to not raise an error because of statistics in your cloudwatch overviews, or because you have the same problem as me that raising an exeption results in a 500 internal server error instead of the desired 401 Unauthorized? One details that seems relevant for my problem is the difference between the api types that AWS api gateway offers (HTTP vs REST), My initial problem is with the HTTP api since there raising leads to a 500 internal server error. – Maarten Derickx Dec 07 '20 at 15:11
  • Yep exactly the same. I want to return 401 Unauthorized but it seems that there isnt any way to do it. Therefore, the only option that I found was raising an exception which will throw an internal error and fail the lambda. – JeyJ Dec 07 '20 at 15:12
  • In the mean time I have attached the exact same authorizer to a REST api and there raising the exact same `Exception("Unauthorized")` error does result in the desired 401 unauthorized response. – Maarten Derickx Dec 07 '20 at 15:12
  • I have contacted AWS support and they confirmed the 500 internal server error instead of the 401 is a bug on their side. They said they have passed it onto the development team and that they are working on the issue. – Maarten Derickx Dec 10 '20 at 10:11
  • great :) Thanks M.D ! – JeyJ Dec 10 '20 at 10:16
  • I just got response from AWS that raising `Exception("Unauthorized")` should now lead to a 401 as expected. I haven't verified this yet. – Maarten Derickx Feb 02 '21 at 08:52
  • @M.D. From reading our previous comments and my original post it seems that it was the original behavior.. – JeyJ Feb 02 '21 at 09:46
  • @JeyJ I guess than we had a different problem! My problem for which I logged an incident at AWS was that when raising `Exception("Unauthorized")` in an authorizer attached to an api gateway of api type "HTTP API" the http statuscode returned was actually a 500 internal server error. Now AWS fixed it so that it now sends a 401 unauthorized back to the caller. Note that this was already the behaviour when the api type of the api gateway was "REST API". So it could be that we were using different api types and therefor experiencing different problems. – Maarten Derickx Feb 03 '21 at 13:48
  • Am I correct in summarizing that my problem was that the api gateway was actually not returning the correct http status code to the caller, while in your example the correct status was actually returned to the caller, but the lambda execution was marked as "Failed" making monitoring way more difficult. – Maarten Derickx Feb 03 '21 at 13:53
  • > I'm trying to return 401 error if the customer didn't send any token, therefore > I'm raising an exception I stumbled upon a similar case yet... I was getting `401` when there was no Authorization header (my custom authorizer wasn't called at all). When I explicitly tampered Bearer token and throw an error then from API Gateway I got `500 Internal Server Error`. Finally, returning `Deny` ended up in `403` with `{"Message":"User is not authorized to access this resource with an explicit deny"}` – iaforek Nov 18 '21 at 13:34

1 Answers1

11

It really is ugly, but that's the only way to truly signal a 401, which means "I can't find your Authorization header or cookie or nothing, you have to authenticate to do that". A 403 is an explicit saying "I know who you are, you're Forbidden from doing that". It's an odd, ternary response that API Gateway needs here: //, and this "throw a very specific exception" is one way to do it.

So you can't customize the response with the authorizer lambda; you can only give a response document that says yay/nay, or throw your hands up and signal "I can't find any authentication material here". To customize the shape of that response to a client, you would use Gateway Responses. With this, you can customize the shape of the json (or whatever content-type, really) of your 401/403 responses.

Now, with respect to raise Exception("Unauthorized") polluting your metrics, making ambiguous real errors vs this expected error, I agree, it kinda stinks. My only recommendation would be to log something ERROR level that you set up a Metric Filter to watch out for, and use that as your true "something's gone wrong" metric.

djcrabhat
  • 464
  • 5
  • 10
  • From some testing it appears it's actually enough to **return** the string `"Unauthorized"` from the lambda handler without any need to raise an exception. After all, the exception example, as far as I can tell, comes from the [one](https://github.com/awslabs/aws-apigateway-lambda-authorizer-blueprints/blob/master/blueprints/python/api-gateway-authorizer-python.py) poorly formatted blueprint and no mention of anything of the sort is made anywhere in the actual docs. – theberzi Feb 01 '23 at 11:32
  • Oh that's an excellent find, @theberzi! And you've got it exactly right, it was found in that blueprint long ago. I coulda sworn it was also in the authorizer docs somewhere, but I'm struggling to find it now. – djcrabhat Feb 10 '23 at 22:29