In part three of the series I discussed creating a serverless REST API – using Lambda and API Gateway – to serve the collected IoT data from the security camera devices.

In this post I will cover securing the REST API.

As a review, the image below provides an overview of the serverless web application that will be implemented using this REST API:

serverless-app-arch-001

Securing the REST API

The API we created in part three needs to be secured to ensure that the videos – and the metadata about those videos – is inaccessible to API users who haven’t been granted access. As importantly, I didn’t want to inject a server to manage user accounts and rights, nor did I want to take on managing another set of user accounts.

The solution: Google Authentication API

I won’t go into significant detail in this post (I will, however, in the next) about implementing the configuration and javascript to authenticate a user with Google and obtain a user token – but suffice it to say once we have the token we can pass it to the REST API and use that to allow/disallow access.

Note: I did look at Cognito, but it seemed like overkill for my purposes.

Creating a API Gateway Custom Authenticator

This is a relatively straightforward process since we will be using a Lambda function as our custom authenticator. So, step one is the creation of another Lambda function using this code from GitHub.

Custom Authenticator Functionality

The custom authenticator does 3 things. First it gets the token sent by the client and makes a HTTP request to google to validate the token and get the user’s information:

    # Get the user info from Google for the recieved token...
    id_token = event['authorizationToken']
    google_token_helper_uri = "https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=" + id_token

    result = json.loads(urllib2.urlopen(google_token_helper_uri).read())

    domain = result['hd']
    user = result['sub']
    effect = 'Deny'

It also sets the default effect to “Deny” – meaning the user will not be allowed access.

The second thing it does is compare some of the user information to a whitelist. In my case I’m granting access to everyone in the brianandkelly.ws domain.

    allowed_domain = "brianandkelly.ws"
    if domain == allowed_domain:
        effect = 'Allow'

This is very simple. This can easily be extended to have a more complex whitelist – including a dynamic one stored in DynamoDB, ActiveDirectory, OpenLDAP or any other backing store.

Third, the authenticator constructs an IAM Policy which either allows or denies access – based on the value of the effect variable – and returns that policy to API Gateway:

    respond = {
        "principalId": user,
        "policyDocument": {
            "Version": "2012-10-17",
            "Statement": [
                {
                    "Action": "execute-api:Invoke",
                    "Effect": effect,
                    "Resource": "arn:aws:execute-api:us-east-1:*:!!your api info!!*"
                }
            ]
        }
    }

    return respond

In the script itself you’ll need to replace !!your api info!! with information from the heading of the API Gateway screen:

screen-shot-2016-10-25-at-6-36-33-pm

The format is:

part in parenthesis/api name - part before parenthisis

in my case:

"Resource": "arn:aws:execute-api:us-east-1:*:7k8o0sgjli/securityvideos/*"

I won’t go into detail about creating this function (we’ve done several in the series) but here is how mine looks fully configured:

screen-shot-2016-10-25-at-6-21-58-pm
Authenticator Lambda Function

Using the existing IAM role we created for the other REST API Lambda Functions will work fine.

You should note that this Lambda Function has no Trigger – since it will be called by API Gateway once we apply the lambda function as a custom authorizer.

Applying the Custom Authenticator to your REST API

The first step is to tell API Gateway to use the Lambda Function as an Authorizer. Click on Authorizers under the API:

Screen Shot 2016-10-25 at 6.44.41 PM.png
Adding Authorizer

Now select “Create” on the right and select “Custom Authorizer”

Screen Shot 2016-10-25 at 6.46.00 PM.png
Custom Authorizer

In the “Update Custom Authorizer” screen select the region your Lambda function is deployed in, the name of the function and give the authorizer a name.

You can leave the default method.request.header.Authorization value in the “Identity token source” field. We will discuss how to set the header in the next post in this series. I strongly recommend that you set “Result TTL in seconds” to a value no less than 300. This is  important for both the performance of your API and to avoid sending excessive requests to Google.

Screen Shot 2016-10-25 at 6.46.55 PM.png
Authorizer Configuration

Finally, you will need to apply the authenticator to all the active methods of your API (or at least the ones you want secure):

Screen Shot 2016-10-25 at 6.51.50 PM.png
Auth setting for a GET Method

You can do that by clicking on “Method Request” and setting the authenticator as follows:

Screen Shot 2016-10-25 at 6.54.07 PM.png

NOTE: You should NOT apply the authenticator to the OPTIONS method. That method is used for CORS and should not be secured.

Deploying the REST API

Once you’ve done that for all the resources you can deploy your API – which will make it available. API Gateway uses “Stage” to denote various types of deployments for your API. You can use these stages to have separate DEV/QA and production stages for your API.

To deploy your API select the root of the resource tree and select “Actions” -> “Deploy API”:

screen-shot-2016-10-25-at-6-56-45-pm
API Deployment

In the “Deploy API” dialog select “[New Stage]”:

screenshot-2016-10-26-13-08-21
New Stage from Deploy

You can then give your stage a name (note: this will appear in the URI), description and a deployment description:

screenshot-2016-10-26-13-08-45
Creating Deploy Stage & Deploying API

Pressing the “Deploy” button completes the process. You should see a stage editor similar to the following:

screenshot-2016-10-26-13-16-29
Deployment Stage Editor

I strongly recommend you tick the boxes for “Enable CloudWatch Logs” and “Enable Detailed CloudWatch Metrics” at the beginning as this will make the logging to CloudWatch verbose enough for you to troubleshoot any issues that arise.

At the top of the stage editor you will find the URI to your new API. You should visit this URI and make sure you are denied access!

Last, but not least, you can test your new API using Postman or Swagger by exporting the API in those formats on the “Export” tab:

screenshot-2016-10-26-13-16-29
Exporting Swagger or Postman API Specification

AWS provides some helpful documentation for testing your API with Postman here. In order to access the API and test it with Postman you’ll need a valid google account and a way to get a valid id_token. This will be discussed in detail in part five of this series when we implement the web application using this API. That being said, if you’d like to jump ahead check out the google documentation and my GitHub repository containing the web application code (the most interesting bit for getting a google id_token is found in init.js).

Summary

We now have fully secured and deployed the REST API created in part three of the series. In part five of the series we will discuss the application built over this API and how to deploy that application serverless.

Leave a comment