DEV Community

Davide de Paolis
Davide de Paolis

Posted on

Basic Auth on Lambda + Api Gateway + Cloudfront: solving the 401 Unauthorized error.

Recently we needed to restrict access /add some basic level of security to an API we are providing to another department. The choice went for Basic Auth ( I know it's kinda old and well.. basic, but the endpoint was supposed to be for internal use anyway )

In order to add this basic authorization in our Lambda handler, we implemented a Middy middleware.
This middleware is in charge of retrieving the authorized users from SSM ParameterStore and then find a match with the token received in the Authorization Header ( by using
basic-auth module )

Unit tests on the middleware as well on the handler were working.
Authorization header was properly parsed and the user was granted access or denied if not in the list ( or if no Auth token was passed)

Once deployed though, nothing was working.

Access Denied

While Testing with Postman the first thing we realized was that Authorization header ( all headers actually) were Capitalized, while the source code of basic-auth was checking for headers.authorization


Headers are case INsensitive.

According to the docs HTTP Headers are case insensitive (see also this discussion on stackoverflow) but honestly, this statement does not make much sense to me.

They might be case insensitive, but when you debug the object in your code, they are either capitalized or lowercase, therefore accessing the property as lowercase or capitalized makes indeed a difference.
It might be APIGateway or Lambda runtime or Node itself, I dunno, but since what we got as Lambda Event was headers.Authorization basic-auth could not find anything under headers.authorization.

I quickly run a test on Gateway API console to check if there was some conversion there:

Execution log for request 299538fb-5d1f-407d-8efb-aadf77e27ae6
Thu Aug 06 10:32:33 UTC 2020 : HTTP Method: GET, Resource Path: /MY_ENDPOINT
Thu Aug 06 10:32:33 UTC 2020 : Method request query string: {foo=1, bar=2}
Thu Aug 06 10:32:33 UTC 2020 : Method request headers: {authorization=*************************bzTjmQ=}
Thu Aug 06 10:32:33 UTC 2020 : Endpoint request body after transformations: {"resource":"/MY_ENDPOINT","path":"/MY_ENDPOINT","httpMethod":"GET","headers":{"authorization":" Basic *************************bzTjmQ="},"queryStringParameters":{"foo":"1","bar":"2"} [TRUNCATED]
Thu Aug 06 10:32:34 UTC 2020 : Endpoint response body before transformations: {"statusCode":401,"body":"Access denied"}
Enter fullscreen mode Exit fullscreen mode

No, no conversion there.

By running a Sample Gateway APIProxy within the Lambda console though, it is clear that all headers are supposed to be capitalized

"headers": {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8",
    "Accept-Encoding": "gzip, deflate, sdch",
    "Accept-Language": "en-US,en;q=0.8",
    "Cache-Control": "max-age=0",
    "CloudFront-Forwarded-Proto": "https",
    "CloudFront-Is-Desktop-Viewer": "true"
Enter fullscreen mode Exit fullscreen mode

It is therefore very likely that the event header passed to the handler has everything capitalized. Whatever, it is just a matter of reading the right property from basic-auth. Luckily they provide an additional method to parse the authorization from any path/object: quickly switch from auth to parse and deploy!

But... The APIGateway URL endpoint was working fine, still when invoking the API through Cloudfront the Access Denied error was still occurring.


After some investigation we found out that for GET requests Cloudfront removes the Authorization header field before forwarding the request to the origin. (see docs)[]

GET and HEAD requests – CloudFront removes the Authorization header field before forwarding the request to your origin.
OPTIONS requests – CloudFront removes the Authorization header field before forwarding the request to your origin if you configure CloudFront to cache responses to OPTIONS requests.
DELETE, PATCH, POST, and PUT requests – CloudFront does not remove the header field before forwarding the request to your origin.

How to change this Behaviour?

Simply whitelist the Authorization header!

From UI Console go to Cache Behaviour Setting and Edit
select Whitelist under Cache Based on Selected Request and then add Authorization under Whitelist Headers

Whitelist Headers on Cloudfront

If, like us, you are deploying with AWS CDK specify which headers must be forwarded by the caching behavior:

              allowedMethods: CloudFrontAllowedMethods.ALL,
              cachedMethods: CloudFrontAllowedCachedMethods.GET_HEAD,
              defaultTtl: Duration.days(1),
              pathPattern: "/MY-ENDPOINT",
              forwardedValues: {
                queryString: true,
                queryStringCacheKeys: ["foo","bar"],
                headers: ["Authorization", "authorization"]
Enter fullscreen mode Exit fullscreen mode

Redeploy, wait a bit for CloudFront to invalidate the distribution and propagate the changes and
request authorization is validated and properly cached for the following request!

happy dance
Hope it helps

Top comments (1)

renanwilliam profile image
Renan William Alves de Paula

Thanks, it helps me