Overview
This document provides a step-by-step guide to working with the provided API Gateway and authenticating the user with AWS Cognito, including how to integrate with Cloudflare. By following the instructions in this document, you will be able to create a fully working CDK stack that creates and connects AWS Cognito and AWS API Gateway
Cloudflare Integration
Cloudflare Integration means that Cloudflare will act as the frontend for our AWS API Gateway. If a client want to send a request to API Gateway, the request will go through Cloudflare first and then AWS API Gateway, and if the request is authorized, it'll hit the AWS Console.
Request from Client to AWS Console diagram
Creating a fully working CDK stack
The follow CDK stack create and integrate AWS Cognito and AWS API Gateway. The steps are:
- Create Cognito User Pool
- Create an app client
- Create a domain
- Create a REST API Gateway
- Create a resource
/ditto
- Create a
GET
HTTP Integration for the resource - Create Cognito authorizer for the API Gateway
- Uses the created Cognito User Pool instance to create the authorizer
- Set the authorization handler for the resource's
GET
method as the created Cognito's authorizer
- Create a resource
CDK Code
View code
import * as cdk from 'aws-cdk-lib'import * as apiGateway from 'aws-cdk-lib/aws-apigateway'import * as cognito from 'aws-cdk-lib/aws-cognito'export class CdkStack extends cdk.Stack {constructor(scope: cdk.App, id: string, props?: cdk.StackProps) {super(scope, id, props)// Create Cognito user poolconst USER_POOL_ID = 'APIgatewayUserPool'const userPool = new cognito.UserPool(this, USER_POOL_ID, {// Allow users to sign-upselfSignUpEnabled: true,autoVerify: {email: true,},standardAttributes: {// By default, Cognito verify sign-ups. When the email isn't required-// Cognito hosted UI won't display it when singing upemail: {required: true,},},})// Add an App Client. Necessary for the hosted API and-// for other apps to interact with Cognito API programmaticallyuserPool.addClient('APIgatewayUserPoolClient')// Create a domain for the Hosted UIuserPool.addDomain('Domain', {cognitoDomain: {// "onetwothree" are meant to make the prefix uniquedomainPrefix: 'apigatewayauthonetwothree',},// You could provide a custom domain instead// customDomain: 'myClient.website.com'})// Create REST API Gatewayconst api = new apiGateway.RestApi(this, 'ditto-api')// Declare HTTP Integration type with target as pokeAPI to be used laterconst httpIntegration = new apiGateway.HttpIntegration('https://pokeapi.co/api/v2/pokemon/ditto')// Add `/ditto` resource to the API Gatewayconst dittoResource = api.root.addResource('ditto')// Create Cognito authorizer for the API Gatewayconst cognitoAuth = new apiGateway.CognitoUserPoolsAuthorizer(this,'ApiGatewayAuth',{cognitoUserPools: [userPool],})// Create a protected GET method with Cognito authorizer for `/ditto` resourcedittoResource.addMethod('GET', httpIntegration, {authorizer: cognitoAuth,authorizationType: apiGateway.AuthorizationType.COGNITO,})}}
Demo API Gateway with auth enabled
The following steps were performed to create the API Gateway with auth enabled:
- Create a REST API
- Create a resource and a method
- Create a Congito user pool
- Create Cognito authorizer from the Authorizers page
- Set the Authorization option in the resource's Method Request to the created Cognito authorizer
Working with the API Gateway
The API Gateway has a custom URL because it'll later be integrated with Cloudflare; Explained in the next section.
API Gateway URL: https://api.flacial.dev
The API Gateway has auth enabled with AWS Cognito. It has one resource named /ditto
that has a GET method with an HTTP integration that respond with a Pokemon from PokeAPI. The resource is protected and to access it, you'll have to provide a JWT token in the Authorization
header.
You can get the JWT token by signing in to Congito's app client with the following credentials:
username: superdude password: Superd3d$
Once you sign in, you'll be redirected to https://example.com, the URL has a query parameter with the name id_token
, this is your JWT token. Save it somewhere.
Usually, it doesn't contain the id_token, but if we set the response_type query parameter as "token" it'll include it
e.g, https://example.com/#id_token=<JWT_TOKEN>...
Using Postman or your favorite API testing tool, send a request to https://api.flacial.dev/ditto with/without the Authorization
header wit the value of id_token
:
With:
curl --location 'https://api.flacial.dev/ditto' \--header 'Authorization: <YOUR_ID_TOKEN>'
Without:
curl --location 'https://api.flacial.dev/ditto'
Common errors
While creating/working with the API Gateway, you may encounter the following issues:
- If the JWT token is valid and you receive the response
message: "unauthorized"
, the JWT token has likely expired. Re-sign in and use the new JWT token. - Make sure when creating the Cloudflare certificate, you set the Hostnames as
<subdomain>.<domain>
e.g,api.flacial.dev
instead offlacial.dev
, otherwise, API Gateway won't be able to intercept your requests from Cloudflare.
Integrating Cloudflare with the API Gateawy
To do this, you'll have to follow the following steps:
- Generate a certificate in Cloudflare
- Import the crertificate in AWS Certificate Manager (ACM)
- Create a custom domain from API Gateway
Custom domain names
page- (this is why our API Gateway domain is api.flacial.dev)
- https://docs.amazonaws.cn/en_us/apigateway/latest/developerguide/how-to-edge-optimized-custom-domain-name.html
- Create an API mapping for your API Gateway and an optional path in your custom domain
- It's basically saying, whenever a request is sent to this custom domain, forward it the selected API Gateway and its stage.
- https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-mappings.html
- Add a
CNAME
DNS record in your Cloudflare page that points a subdomain to the API Gateway domain name - Set the encryption mode as Full
- This is because AWS only supports an SSL certification, and not a valid, publicly trusted certificate. This info require more research.
- https://developers.cloudflare.com/ssl/origin-configuration/ssl-modes/