Skip to content

Repository files navigation

cdk-cognito-m2m-proxy

AWS CDK construct package for setting up a cached Amazon API Gateway proxy endpoint with Cognito machine-to-machine (M2M) authentication.

Getting Started

Installation

To use cdk-cognito-m2m-proxy in your AWS CDK TypeScript project, install it via npm:

npm install --save-dev cdk-cognito-m2m-proxy

This package has the following peer dependencies, which must also be installed in your project:

  • aws-cdk (^2.1013.0)
  • aws-cdk-lib (^2.193.0)
  • constructs (^10.4.2) You can install them together using:
npm install --save-dev aws-cdk@^2.1013.0 aws-cdk-lib@^2.193.0 constructs@^10.4.2

Usage

For detailed example usage, please refer to the L2 construct usage and L3 construct usage sections below.

Introduction

As AWS announced, a new pricing model has been introduced for AWS Cognito's Machine-to-Machine (M2M) authentication services. Under this model, costs are calculated based on two dimensions (see detailed pricing):

  • A monthly base fee per app client (prorated to the second)
  • A per-transaction fee for each successful token response

For applications with frequent token requests, these transaction costs can accumulate rapidly. To help reduce these expenses, AWS officially recommends implementing token caching.

This package provides two AWS CDK constructs that implement token caching via an API Gateway proxy layer. These constructs handle the complexity of setting up a secure and efficient caching mechanism, requiring only that you redirect your token requests to the provided API Gateway endpoint instead of the Cognito endpoint.

Token Request Flow

When using these constructs, client applications should direct their token requests to the API Gateway endpoint rather than directly to Cognito:

sequenceDiagram
    autonumber
    participant client as Client
    participant proxy as API Gateway (proxy)
    participant cognito as Cognito User Pool
    participant api as Protected API

    %% Step 1: Client requests token for M2M
    client->>proxy: Request Cognito Token
    alt token not cached
        proxy->>cognito: Authenticate (M2M credentials)
        cognito-->>proxy: ID/Access Token
        proxy-->>proxy: Cache token
        proxy-->>client: Token
    else token is cached
        proxy-->>proxy: Retrieve token from cache
        proxy-->>client: Token from cache
    end

    %% Step 2: Client uses token to call protected API directly
    client->>api: Call Protected Endpoint (with Cognito token)
    api-->>client: API Response
Loading
  1. The client requests a token from the API Gateway endpoint.
  2. The API Gateway checks if a matching token response is already cached:
    1. If cached: Returns the cached token (no Cognito charges incurred).
    2. If not cached: Forwards the request to Cognito, caches the response, and then returns the token.
  3. Subsequent identical requests within the cache TTL period are served from the cache.

Constructs

For existing Cognito user pools, the L2 construct CognitoM2MTokenCacheProxy adds caching capabilities without requiring modifications to the user pool configuration. For new implementations, the L3 construct CognitoM2MWithTokenCache provisions both a Cognito user pool and an integrated caching layer in a single, cohesive solution.


L2: CognitoM2MTokenCacheProxy

This Level 2 (L2) construct creates an API Gateway with caching enabled to serve as a proxy for Cognito token requests. It's designed for use with an existing Cognito user pool, allowing you to add token caching without modifying your current Cognito setup.

Example Usage

// For an existing Cognito user pool
const tokenCacheProxy = new CognitoM2MTokenCacheProxy(this, 'TokenCacheProxy', {
  stage: props.stage,
  cognitoTokenEndpointUrl: 'https://your-domain.auth.region.amazoncognito.com/oauth2/token',
  cacheTtl: Duration.minutes(55), // Set slightly below your token expiration time
  cacheSize: '0.5', // For light to moderate usage
  namePrefix: 'myapp',
  customDomain: {
    domainName: 'example.com',
    subDomain: 'auth',
    certificate: myCertificate, // ACM certificate for the domain
    hostedZone: myHostedZone // Route 53 hosted zone
  }
});

Configuration Options

The CognitoM2MTokenCacheProxyProps interface provides the following configuration options:

Property Type Description Required
stage string Deployment environment (e.g., 'dev', 'staging', 'prod') Yes
cognitoTokenEndpointUrl string Full URL to your Cognito token endpoint Yes
cacheTtl Duration Time-to-live for cached tokens Yes
cacheSize string API Gateway cache size in GB (e.g., "0.5", "1.6", etc.). See AWS Docs. No, default: "0.5"
namePrefix string Optional prefix for resource names No
enableAuthorizationHeaderValidation boolean Flag to enable Authorization header validation No
customDomain object Configuration for custom domain setup No

Tip

OAuth2 standard recommends using the Authorization header for client credentials. Refer to RFC6749. Using enableAuthorizationHeaderValidation will enforce Authorization header.


L3: CognitoM2MWithTokenCache

This Level 3 (L3) construct creates a Cognito user pool and an API Gateway with caching enabled as a proxy for token requests. The user pool is exposed for integrating with app clients, resource servers, and other resources.

Example Usage

const serviceUserPool = new CognitoM2MWithTokenCache(this, 'CognitoPoolWithM2MCache', {
  stage: props.stage,
  cacheTtl: Duration.minutes(50),
  customCacheAPIDomain: {
    domainName: 'example.com',
    subDomain: 'auth',
    certificate: myCertificate, // ACM certificate for the domain
    hostedZone: myHostedZone // Route 53 hosted zone
  }
});

Configuration Options

The CognitoM2MWithTokenCacheProps interface provides the following configuration options:

Property Type Description Required
stage string Deployment environment (e.g., 'dev', 'staging', 'prod') Yes
cacheTtl Duration Time-to-live for cached tokens Yes
cacheSize string API Gateway cache size in GB (e.g., "0.5", "1.6", etc.). See AWS Docs. No, default: "0.5"
namePrefix string Optional prefix for resource names No
userPoolProps CognitoUserPoolProps Optional properties for custom Cognito User Pool settings No
enableAuthorizationHeaderValidation boolean Flag to enable Authorization header validation No
customCacheAPIDomain object Configuration for custom domain setup No

Tip

OAuth2 standard recommends using the Authorization header for client credentials. Refer to RFC6749. Using enableAuthorizationHeaderValidation will enforce Authorization header.

The construct exposes the user pool so it can be used to create app clients, resource servers, etc., as you normally would.

// Example: Create a custom resource server scope
const todoReadScope = new ResourceServerScope({
  scopeName: "todo:read",
  scopeDescription: "todo read scope",
});

// Attach the scope to a resource server
const todoApiResourceServer = new UserPoolResourceServer(this, `TodoApi`, {
  identifier: `TodoApi`,
  userPool: serviceUserPool.userPool, // Reference from the construct
  scopes: [todoReadScope]
});

// Create an app client that uses the scope
new UserPoolClient(this, `AppClient`, {
  userPool: serviceUserPool.userPool, // Reference from the construct
  accessTokenValidity: Duration.minutes(60),
  generateSecret: true,
  refreshTokenValidity: Duration.days(1),
  enableTokenRevocation: true,
  oAuth: {
    flows: {
      clientCredentials: true,
    },
    scopes: [
      OAuthScope.resourceServer(
        todoApiResourceServer,
        todoReadScope
      )
    ],
  },
});

Cache Considerations

  • Use a cache TTL slightly below your token expiration time to ensure optimal caching while avoiding expired tokens.
  • Choose a cache size appropriate for your workload:
    • '0.5' for light to moderate usage (up to ~1000 requests/second). Start small and scale as needed; 0.5 GB is often sufficient.
    • Larger sizes are available for higher throughput. Note: Different sizes incur different costs.
  • Tokens are cached based on authorization header and scope parameters, ensuring proper isolation between different clients.

Cache Keys

These constructs use a cache key that includes the following parameters:

Cache Key Name Type Description
Authorization Header The Authorization header containing client credentials (e.g., Basic Auth).
Content-Type Header The Content-Type header specifying the format of the request body (e.g., application/x-www-form-urlencoded).
scope Query String The scope query string parameter defining the requested permissions.
grant_type Query String The grant_type query string parameter specifying the OAuth grant type.
client_secret Query String The client_secret query string parameter for client authentication.
client_id Query String The client_id query string parameter identifying the client application.
bodyCacheKey Custom A custom parameter derived from the POST request body to ensure cache uniqueness even if parameters are sent in the body.

About

AWS CDK construct for setting up a cached Amazon API Gateway proxy endpoint with Cognito machine-to-machine (M2M) authentication.

Topics

Resources

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages