/rest-api-gateway-jwt-cognito

CDK (TypeScript) sample on how to configure API Gateway (REST API) with JWT token auth via Amazon Cognito and Lambda Authorizers

Primary LanguagePythonMIT No AttributionMIT-0

rest-api-gateway-jwt-cognito

This sample shows how to integrate JWT token authorization with Amazon API Gateway utilizing AWS CDK.

The flavor of API used in this sample is the REST API. The REST API type offers more endpoint types, more security features, better API management capabilities, and more development features when compared to the HTTP API type. More details about the comparison can be found in the documentation.

To be able to use JWT authorization in your REST API, one additional component needs to be added. That is the authorizer Lambda function. This sample shows how to set up the authorizer function.

In contrast, the HTTP API has native support for validating JWT tokens (no need to set up an authorizer function). There is another sample which demonstrates how that can be done too.

The token issuing service used in this sample is Amazon Cognito.

This solution is native to AWS, and there is no need to set up a separate JWT token issuer/service. Cognito handles User management and the generation of their tokens via User Pools.

A notable advantage of using the REST API flavor (of API Gateway) is that it natively integrates with AWS WAF; and does not require additional components and/or configuration to get that working.

Architecture

Architecture

The typical workflow would be that a client application (or a user) would request a JWT token from Cognito using credentials (such as a username and password).

That token would then be supplied in the request header destined for the API Gateway endpoint. Since this sample uses the REST API, it first invokes the authorizer lambda function to check if the token presented is valid or not. If it is valid, the request is then forwarded to the AWS service integration of choice.

API Gateway can integrate with many AWS Services. In this sample, it is connecting to a simple lambda function which returns a simple "Hello" message.

Seeing it in action

Pre-requisites

  • Since this is a TypeScript CDK project, you should have npm installed (which is the package manager for TypeScript/JavaScript).

    • You can find installation instructions for npm here.
  • Additionally, it would be required for your to have AWS CLI installed on your computer.

    • pip install awscli. This means need to have python installed on your computer (if it is not already installed.)
    • You need to also configure and authenticate your AWS CLI to be able to interact with AWS programmatically. Detailed instructions of how you could do that are provided here
  • Lastly, you would need to have docker installed on your computer. This is needed because the authorizer lambda function is created using docker (for easy installation of 3rd party python libraries like PyJWT and Cryptography). Note - you can create it using lambda layers (for the installation of 3rd party Python libraries) as well but this sample uses docker.

    • You can check out these options for building and running docker containers:
      • Docker desktop. Most popular container management app. Note - it does require a license if the organization you work at is bigger than a certain threshold.
      • Rancher desktop. It is a popular open source container management tool.
      • Finch. Another open-source tool for container management.Note - currently it only supports MacOS machines.

Install dependencies (if not already done)

npm install

Set environment variable (if you are on an M1/M2 Mac)

Depending on the architecture of your computer, you may need to set this environment variable for the docker container. This is because docker containers are dependent on the architecture of the host machine that is building/running them.

If your machine runs on the x86 architecture, you can ignore this step.

export DOCKER_CONTAINER_PLATFORM_ARCH=arm

Deploy the infrastructure

# make sure you are in the root directory of this project

npx cdk deploy RestApiGatewayJwtCognitoStack

# you can optionally specify the --profile flag to specify a specific non-default AWS profile

This will deploy:

  • A Cognito User Pool, and an application integration client associated with that user pool
  • An API Gateway (HTTP API), and a Lambda function
  • Role(s) for API Gateway and Lambda function

The code defining this infrastructure is in the http-api-gateway-jwt-cognito-stack.ts file in the lib directory.

The invocation URL for the API can be found in the API Gateway console (specifically in the Stages section). You will need it to test the API request.

Create a new user in the Cognito user pool

  • You can do this in the Cognito console.

  • You can also use the AWS CLI for this. The cognito-idp API reference is here. Specific calls are:

  • There is another utility from aws-samples you can use to ease user creation. It is called cognito-user-token-helper. You can use that script to create a new user by using the -a create-new-user flag and supply the --user-pool-id with the ID of the User pool that was created as part of the infrastructure deployment. More detailed instructions are provided in that project.

Obtain a JWT token

After you've created a user in the Cognito User Pool, you can request for a JWT ID token for the API.

  • This can be done programmatically via the AWS CLI. You need to invoke an auth initiation like admin-initiate-auth. You can obtain the "IdToken" as the JWT token from the response of the API call.

  • The cognito-user-token-helper utility is another option that you can use to obtain a token from cognito. You can use the -a generate-token flag, and supply the --user-pool-id with the ID of the user pool, and supply the --client-id flag with the application integration client ID. Both the User Pool and Application Integration Client are created as part of the infrastructure deployment.

Test the authorized API endpoint

Once the token is obtained, you can now send a request to the API via Postman, cURL, HTTPie etc.

The endpoint is a simple GET request on the /hello route.

Authorized request

The example shown below is using curl

curl <INVOKE_URL_FROM_API_GATEWAY>/hello -H "Authorization: Bearer <JWT_TOKEN_STRING_GOES_HERE>"

If the request was successful, the output should look like:

"Hello from Lambda!"

Unauthorized request

The example shown below is using curl

curl <INVOKE_URL_FROM_API_GATEWAY>/hello 

# NOTE - the authorization header was not provided in this request

The output should look like:

{"message":"Unauthorized"}

Generic CDK instructions

This is a blank project for CDK development with TypeScript.

The cdk.json file tells the CDK Toolkit how to execute your app.

Useful commands

  • npm run build compile typescript to js
  • npm run watch watch for changes and compile
  • npm run test perform the jest unit tests
  • cdk deploy deploy this stack to your default AWS account/region
  • cdk diff compare deployed stack with current state
  • cdk synth emits the synthesized CloudFormation template