How to make custom IAM authenticated requests to AWS API Gateway

Christopher ImrieBy Christopher Imrie on April 8th 2017

Kingfisher

Overview

  • Authenticate custom HTTP requests to your API Gateway that are protected with IAM authentication
  • Enables you to bring your own Http library such as Angular Http, HTML5 fetch, jQuery etc while still using API Gateway
  • Demonstrates how to use sub libraries of the AWS SDK to generate the required Authorization header
  • Examples are for browser based applications or NodeJS, both using Javascript

Introduction

When working within Amazon Web Services (AWS), the Identity and Access Management (IAM) controls everything. Configuring IAM effectively is critical to achieving a secure infrastructure configuration, but web application occasionally trip up when attempting to make IAM authenticated requests to an API Endpoint; its not always clear how exactly to sign a request. Its tempting to simply use API Keys (AKA Usage Plans), implement a custom solution or just leave the API public.

Once setup, API Gateway itself is able to generate an SDK for you, but this isn’t always ideal since:

  • Doesnt let you choose your own http library (SDK uses static functions with callbacks)
  • Requires you to continually update the SDK as the API Gateway is updated (ie tight coupling)

How To

Assuming your API Gateway endpoint is already setup and configured with IAM Authentication, in your application load the AWS SDK as per the installation instructions and configure the credentials as per your use case.

If you are wanting to keep file size small in the browser, I recommend using the Custom AWS SDK Builder and including just the AWS.CognitoIdentity service.

Build a fake request

Now that the AWS SDK is loaded, begin by creating an AWS Request object. You will need the URL to your API Gateway endpoint and the region it is in:

//HttpRequest representation of your request (this wont actually _DO_ the request)
var httpRequest = new AWS.HttpRequest("https://xxxxxxx.execute-api.eu-west-1.amazonaws.com/uri/to/method", "eu-west-1");

//REQUIRED
//Host & content type headers must be set
httpRequest.headers.host = "xxxxxxx.execute-api.eu-west-1.amazonaws.com"; //Host of the API being called
httpRequest.headers['Content-Type'] = "application/json";

//OPTIONAL
httpRequest.method = "GET"; //Default is POST
httpRequest.headers['x-biz'] = 'baz'; //Additional headers
httpRequest.body = JSON.stringify({foo:'bar'}); //Body must be a string if being used

Load credentials

If not done already, you will need to prime the AWS SDK configuration to fetch credentials. Depending on your credentials type this does various things ranging from fetching session tokens (is using Cognito) to nothing (if using hard coded access & secret keys).

Either way, its best practice to do this in order to avoid unexpected behaviour:

//Instruct cognito credentials to get access, secret and session keys (only needs to be done once on page load)
AWS.config.credentials.get(function(err){
    //If err is null, Credentials are ready for use

    //Signing code code here....
});

Sign the request

Once credentials are ready, you can use the AWS.Signers functions to sign the request.

The AWS SDK comes with many different signers due to the different generations used across all APIs provided by AWS. The API Gateway always uses the V4 signer:

//All IAM Authorised API Gateway requests use the V4 signer
var v4signer = new AWS.Signers.V4(httpRequest, "execute-api", true);
v4signer.addAuthorization(AWS.config.credentials, AWS.util.date.getDate());

Extract details

Once the signing process has completed, you can pick apart the httpRequest object in order to apply its properties to your Http library of choice:

//Pick it apart and use your own AJAX/Fetch library
//The AWS.HttpRequest itself isn't able to make the request on its own
console.log(httpRequest.headers) // { authorization: 'AWS4-HMAC-SHA256 Credential=ASIA...', x-amz-security-token:'AgoGb...', ...}
console.log(httpRequest.method) // GET
console.log(httpRequest.endpoint.href) // https://xxxxxxx.execute-api.eu-west-1.amazonaws.com/uri/to/method
console.log(httpRequest.body)

Complete example

Here is a complete example as a Github Gist.

Angular 2 Service example

To see the benefits of the technique, below is an example of how to use it with the Angular 2 Http library and import it as a service:

Its not just for private APIs

With the simplicity demonstrated in the above examples you should consider using IAM authentication for APIs used by a web applications with public/non authenticated users. Implementing IAM authorisation into your APIs using Cognito Unauthenticated identities gives your APIs an additional layer of protection that helps ensure only legitimate requests are allowed through.

If you need to implement this serverside in NodeJS (perhaps to access a microservice you built) the implementation is even simpler since you can sidestep Cognito and directly use an AWS IAM User Access & Secret keys.

You can hire me

Contract or consultancy basis, you can benefit from my expertise.

Whether you need full stack development for apps, APIs, Serverless infrastructure, AWS environment development, cloudformation or continuous integration, I can help.

Email