There will always be the cases where your Amazon API Gateway endpoints, or even generally speaking some of your API endpoints hosted wherever, have to be protected from the public. Some things are just not meant for the outside world (yet!) and they have to be kept away. There are multiple reasons to this, i.e. protected endpoints serve as your sandbox/test systems, data/information protection, maintaining of business reputation amongst end users, keeping low infrastructure costs/bills e.t.c. The lastly mentioned reason will be relevant in this article, especially in relation to the Amazon WebServices (AWS) infrastructures.

A quiet straightforward way to protect your endpoint is by integrating the HTTP Basic Authentication. It might seem like a no-brainer, but integrating this into your Amazon API Gateway proxy might be rather tedious.

This article explains how set up a simple but effective configuration on the AWS ecosystem using an endpoint proxy hosted through the Amazon API Gateway with the following instructions below.

Before jumping into the AWS world, let me explain shortly how the HTTP basic authentication works.

HTTP basic authentication

Source: https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication Source: https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication

Client tries to access a protected endpoint/URL. The server responds with a 401 HTTP code and includes the WWW-Authenticate header set to Basic as header value. The client is prompted via the browser (in a dialog) to provide a username and password. The basic credentials are sent back with the same request to server in the Authorization header with Basic <token> value, being <token> the Base64 encoded representation of username:password. The server validates the sent token and finally gives the client access to the protected endpoint upon correct validation.

Instructions on how to set up proxy authentication in Amazon API Gateway

After setting up the proxy resources/endpoints on Amazon API, follow these steps to protect the endpoint using the basic HTTP authentication:

Add the WWW-Authenticate header set to Basic to the Gateway Responses / Unauthorized (401) section of the endpoint configuration.

Gateway Responses / Unauthorized (401)

This is enough to “tell” the browser to display the username-password dialog when the API gateway does not authorize a client. Now, the API has to validate the token sent in Authorization header the as explained above. This brings us to the next configuration, the so called Custom/Lambda Authorizers. Before we move on in creating a new custom authorizer, we have to create a new AWS Lambda function which is going to hold the validation code for the token. For now, we can simply create a function that does nothing (visit https://eu-central-1.console.aws.amazon.com/lambda/home?region=eu-central-1#/functions or switch to a region close to you after visiting that link) and press on the Create Function button.

Create Function

Here, as runtime, we are using Ruby 2.5, but any other runtime of your choice can be used. Create the function. We will come back to that shortly. At first, we have to finish creating the custom authorizer. Now, we go back to the AWS API Gateway to create the authorizer. The new custom authorizer can be created under the Authorizers section of the endpoint. Here, you have to provide

  • a name for your authorizer
  • corresponding lambda function name - it explains why they are also referred to as “lambda authorizers”
  • token as your payload type - since we want to simply read from the Authorization header
  • token source - which is basically the Header name where the token should be read from. Here, Authorization or method.request.header.Authorization

Leave the rest as defaults.


NOTE

If AWS CloudFront is integrated into your AWS ecosystem, make sure to forward all relevant headers, see this developer guide for more information, especially this section

Create Authorizer

Save the configuration and add this to the endpoint that needs protection. This can be done under the Resources section of the endpoint. Click on the HTTP Method and then Method Request to change configurations.

API configuration 1

API configuration 2

That’s in the AWS API Gateway environment! Now we implement our Lambda function (see link at the end for full implementation)

def lambda_handler(event:, context:)

end

The function receives two parameters

  • context which contains information regarding the function itself, e.g. function name, time remaining till timeout e.t.c.
  • event which contains information sent from the API Gateway - the Amazon Resource Name (ARN), token and the type of authorizer(TOKEN or REQUEST) - in our case TOKEN.

An example of the information in the event-parameter is

{"type"=>"TOKEN", "authorizationToken"=>"Basic xxxxxxxxxx", "methodArn"=>"arn:aws:execute-api:eu-central-1:account-id:abcdefgh/*/*/*"}

As a result of this Lambda function, the caller (API Gateway) expects a valid JSON representation of a policy document which contains the information if the request is authenticated (Allow) or not (Deny).

{
    "principalId": "valid_username_abd",
    "policyDocument": {
      "Version":"2012-10-17",
      "Statement":[
          {
            "Effect":"Allow",
            "Action":"execute-api:Invoke",
            "Resource":"arn:aws:execute-api:eu-central-1:account-id:abcdefgh/*/*/*"
          }
      ]
  },
  "context": {
    "customKey": "customValue"
  }
}
  • principalId contains information on the validated username
  • policyDocument contains the document version (see IAM JSON Policy Elements: Version) and statements on the actions that are allowed or denied for which resources. The document can contain an array of statements (see IAM JSON Policy Elements: Statement).
  • context optional, contains further information we might want to pass back to the caller.

Now save your Lambda function and see HTTP Basic Authentication come in action when you try to access the protected proxy URL.

A complete implementation of such a Lambda function using Ruby can be found here: https://gist.github.com/babajidemm/1ceb3e9d3470dbdced87749f4de81030