Using the API Gateway Authorizer with multi user-pools
Lucas Ferreira
Posted on September 24, 2021
Context
I've been working on a project with a multi-tenancy architecture, and during the refactor from single-tenant to multi-tenant, we had a problem with AWS Cognito and the AWS API Gateway Authorizer.
The problem was, we wanted to add multi Cognito User Pools to our Authorizer and use it to check the token in Cognito, something like this:
- With SDK:
import { APIGateway } from 'aws-sdk';
const update_authorizer = async (apigateway: APIGateway, op: 'remove' | 'add') => {
const params: APIGateway.Types.UpdateAuthorizerRequest = {
authorizerId: 'x99xxx', /* required */
restApiId: 'xxxx99xxxx', /* required */
patchOperations: [ // adding all user pools to the same authorizer
{ // CognitoUserPoolTenantA
op,
path: '/providerARNs',
value: 'arn:aws:cognito-idp:us-east-1:xxxxxxxxxxxx:userpool/us-east-1_xxxxxxxxx',
},
{ // CognitoUserPoolTenantB
op,
path: '/providerARNs',
value: 'arn:aws:cognito-idp:us-east-1:xxxxxxxxxxxx:userpool/us-east-1_yyyyyyyyy',
},
// and keep going adding new User Pools
],
};
await apigateway.updateAuthorizer(params).promise();
};
// calling the script
const apigateway = new APIGateway({ region: 'us-east-1' });
await update_authorizer(apigateway, 'add');
- With Cloud Formation:
Resources:
ApiGatewayAuthorizer:
DependsOn:
- SharedApiGateway
Type: AWS::ApiGateway::Authorizer
Properties:
Name: Authorizer
IdentitySource: method.request.header.Authorization
RestApiId:
Ref: SharedApiGateway
Type: COGNITO_USER_POOLS
ProviderARNs:
- Fn::GetAtt: ['CognitoUserPoolTenantA', Arn]
- Fn::GetAtt: ['CognitoUserPoolTenantB', Arn]
# keep going adding new User Pools in the file
# or adding with the SDK.
Problems during implementation
The API Authorizer has a default cache with the time for invalidation equal to 300 seconds. See more here.
After the changes made by the
update_authorizer()
function, we saw that the tokens were taking some time to work until we found thecreateDeployment()
inside the AWS SDK for API Gateway.
Final solution
We had to add new functions to the same file where we wrote the first function (update_authorizer).
// ...
const release_api_deployment = async (apigateway: APIGateway) => {
const params: APIGateway.Types.CreateDeploymentRequest = {
restApiId: 'xxxx99xxxx', /* required */
stageName: 'dev',
};
await apigateway.createDeployment(params).promise();
};
const flush_authorizers = async (apigateway: APIGateway) => {
const params: APIGateway.Types.FlushStageAuthorizersCacheRequest = {
restApiId: 'xxxx99xxxx', /* required */
stageName: 'dev', /* required */
};
await apigateway.flushStageAuthorizersCache(params).promise();
};
// final call
const execute = async () => {
const apigateway = new APIGateway({ region: 'us-east-1' });
await update_authorizer(apigateway, 'remove');
await flush_authorizers(apigateway); // clean cache
await release_api_deployment(apigateway); // release a new version of the API
// ... and to test the API we need to generate new tokens
};
try {
execute();
} catch (error) {
console.error(error);
}
So with those scripts, we can update the API Gateway Authorizer
to works with multi Cognito User Pools without implement a Custom Authorization Lambda.
Posted on September 24, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.