This is a series of articles about setting up a complex Serverless backend infrastructure with AWS SAM and CloudFormation.
Here is the index of all the articles in case you want to jump to any of them:
- Setup of AppSync and API Gateway with Multiple AWS Cognito User Pools
2. Configuring S3 Buckets with Permissions and Access Roles in AWS Cognito AuthRole
3. Intro to DynamoDB Resolvers for AppSync Implementation
4. Intro to Lambda Resolvers for AppSync Implementation
5. Configuring an AWS VPC to Include Lambda Resolvers with a Fixed IP
6. Intro to Pipeline Resolvers for AppSync Implementation
7. Handling Lambda Resolver Timeouts with SNS Messages
You have two types of users — regular customers and back-office admins. They need different permissions, different login flows, and different security policies. Cramming both into a single Cognito User Pool means group-based hacks and policies that are hard to audit.
Two separate pools, one API. Each pool owns its own authentication rules. AppSync and API Gateway both support multiple Cognito providers natively.
For a deeper dive into the Cognito configuration itself, see my previous article: Configuring Two Cognito User Pools for a Single AppSync GraphQL API
AppSync with Two User Pools
Create both pools, then wire them into AppSync — one as the primary authentication provider, one as an additional provider:
Resources:
# Cognito User Pools
UserPoolGeneral:
Type: "AWS::Cognito::UserPool"
Properties:
UserPoolName: "GeneralUsers"
Schema:
- Name: email
AttributeDataType: String
Mutable: true
Required: true
AutoVerifiedAttributes:
- email
UserPoolBackoffice:
Type: "AWS::Cognito::UserPool"
Properties:
UserPoolName: "BackofficeUsers"
Schema:
- Name: email
AttributeDataType: String
Mutable: true
Required: true
AutoVerifiedAttributes:
- email
# AppSync API
AppSyncAPI:
Type: "AWS::AppSync::GraphQLApi"
Properties:
Name: "MyAppSyncAPI"
AuthenticationType: "AMAZON_COGNITO_USER_POOLS"
UserPoolConfig:
UserPoolId: !Ref UserPoolGeneral
AwsRegion: !Ref "AWS::Region"
AdditionalAuthenticationProviders:
- AuthenticationType: "AMAZON_COGNITO_USER_POOLS"
UserPoolConfig:
UserPoolId: !Ref UserPoolBackoffice
AwsRegion: !Ref "AWS::Region"
AppSyncSchema:
Type: "AWS::AppSync::GraphQLSchema"
Properties:
ApiId: !GetAtt [AppSyncAPI, ApiId]
Definition: |
type Query {
getData: String
}
schema {
query: Query
}API Gateway with Both Pools
Same idea — a Cognito authorizer that accepts tokens from either pool:
Resources:
# ...
# API Gateway
MyApi:
Type: 'AWS::ApiGateway::RestApi'
Properties:
Name: 'MyAPI'
Description: 'My API service.'
FailOnWarnings: 'true'
RootMethod:
Type: 'AWS::ApiGateway::Method'
Properties:
AuthorizationType: 'COGNITO'
AuthorizerId: !Ref CognitoAuthorizer
HttpMethod: 'ANY'
ResourceId: !GetAtt [MyApi, RootResourceId]
RestApiId: !Ref MyApi
CognitoAuthorizer:
Type: 'AWS::ApiGateway::Authorizer'
Properties:
Name: 'CognitoAuthorizer'
IdentitySource: 'method.request.header.Authorization'
RestApiId: !Ref MyApi
Type: 'COGNITO_USER_POOLS'
ProviderARNs:
- !GetAtt [UserPoolGeneral, Arn]
- !GetAtt [UserPoolBackoffice, Arn]Two pools, clean separation, zero group-based workarounds. Admins authenticate against the admin pool with stricter password policies and MFA. Regular users authenticate against theirs. The API accepts tokens from both and the schema-level directives control who can access what.
Code examples are simplified to illustrate the approach. Some adjustments may be needed for production.
Next UP: Part 2. Configuring S3 Buckets with Permissions and Access Roles in AWS Cognito AuthRole
