When building serverless applications, you'll often face the challenge: "How can I assign different permissions to different users in a secure and scalable way?" This becomes especially critical for services like B2B SaaS platforms or e-commerce systems where user roles are clearly defined.
Today, I'll show you how to implement a group-based permission management authentication system using Amazon Cognito User Pools and Identity Pools with CDK. We'll walk through everything step-by-step with a working example you can deploy and test.
Why Do We Need This Pattern?
Limitations of Traditional Approaches
Common serverless authentication implementations often run into these issues:
Simple JWT Token Approach:
User → API Gateway → Lambda → Permission Check → AWS Resource Access
- Constant Permission Checks: Every API call requires Lambda to verify permissions
- Scalability Issues: Complexity grows exponentially as users and permissions increase
- Security Risks: Token management and permission logic scattered across the application
Custom Authentication Systems:
- Development Cost: Implementing registration, login, password reset, and all auth features from scratch
- Security Vulnerabilities: Handling authentication security issues yourself
- Operational Overhead: Managing user data, sessions, and infrastructure
The Cognito Pattern Solution
User → Cognito User Pool → Cognito Identity Pool → Direct AWS Resource Access
(Authentication) (Authorization) (IAM-based Permissions)
The core idea is "complete separation of authentication and authorization."
Real Implementation Example: Seller/Buyer System
Let's examine the GitHub repository example, which implements a seller/buyer permission system for an e-commerce platform:
Processing Flow
- User Registration: Users select either seller or buyer group during signup
- Group Assignment: Cognito User Pool automatically adds user to the selected group
- Authentication: Login issues JWT token (including group information)
- Credential Exchange: Identity Pool issues temporary AWS credentials based on group info
- Resource Access: Direct access to AWS resources like S3 according to IAM conditional policies
Permission Structure
In this system, sellers are granted read and write access to S3, allowing them to upload product images and files necessary for registering and managing their products. Meanwhile, buyers receive read-only access, enabling them to browse and view product information without the ability to modify or upload content.
Response Examples
Temporary Credentials Response (Actual AWS Response):
{
"AccessKeyId": "ASIA...",
"SecretAccessKey": "...",
"SessionToken": "...",
"Expiration": "2025-07-13T15:00:00.000Z"
}
Important: When you use these credentials, access is granted or denied by AWS services based on the IAM role's permissions. Permission information is not directly included in the credentials themselves—it's enforced by AWS backend through IAM policies.
Core Architecture and Components
Architecture Diagram
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ User Pool │─│ Identity Pool │─│ AWS Resources │
│ │ │ │ │ │
│ ┌─────────────┐ │ │ ┌──────────────┐ │ │ • S3 Buckets │
│ │seller group │ │─│ │ Conditional │ │─│ • DynamoDB │
│ └─────────────┘ │ │ │ IAM Policies │ │ │ • Lambda │
│ ┌─────────────┐ │ │ └──────────────┘ │ │ • etc... │
│ │buyer group │ │ │ │ │ │
│ └─────────────┘ │ └──────────────────┘ └─────────────────┘
└─────────────────┘
Key Components
- Cognito User Pool (Authentication)
- Manages user registration and login
- Email verification and password policies
- Group-based user management
- JWT token issuance
- Cognito Identity Pool (Authorization)
- Exchanges User Pool JWT tokens for AWS credentials
- Maps groups to IAM roles
- Issues temporary credentials (15 minutes to 12 hours)
- CloudFront + S3 (Frontend)
- Static web application hosting
- Fast loading through global CDN
- HTTPS enforcement
- IAM Conditional Policies
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject"
],
"Resource": "*",
"Condition": {
"ForAnyValue:StringEquals": {
"cognito:groups": ["seller"]
}
}
}
]
}
Special Design Principles
Zero-Trust Architecture
- Verify user group for every request
- Permission verification at AWS service level
- Remove permission logic from application code
Leveraging Temporary Credentials
// Users can directly access AWS resources using AWS SDK
const s3 = new AWS.S3({
accessKeyId: credentials.AccessKeyId,
secretAccessKey: credentials.SecretAccessKey,
sessionToken: credentials.SessionToken
});
// Access control automatically enforced based on group permissions
await s3.putObject({
Bucket: 'my-bucket',
Key: 'file.txt',
Body: 'content'
}).promise();
When Should You Use This Pattern?
Suitable Use Cases
Role-Based B2B SaaS Applications
- Clear role distinctions like admin, editor, viewer
- Different AWS resource access permissions per role
- Dashboards, analytics tools, CRM systems
E-commerce Platforms
- Buyers: Browse products, create orders, process payments
- Sellers: Register products, manage inventory, view sales analytics
- Admins: Access all data and system management
Multi-Tenant Applications
- Data isolation per tenant
- User role management within tenants
- Resource usage monitoring and limits
Content Management Systems
- Authors: Create and edit content
- Reviewers: Approve or reject content
- Readers: View-only access to content
Unsuitable Cases
Real-Time Interaction Systems
- Chat applications, real-time games
- WebSocket-based services
- Systems requiring millisecond response times
Simple CRUD Applications
- Systems with only single roles
- Cases where complex permission management is unnecessary
- Prototypes or personal projects
Synchronous Processing Requirements
- Payment authorization (immediate approval/rejection needed)
- Real-time inventory management
- Financial transaction systems
Hands-On Practice
You can implement and test this with the provided GitHub repository:
Prerequisites
# Check Node.js 18+ installation
node --version
# Configure AWS CLI
aws configure
# Install CDK CLI
npm install -g aws-cdk
Step-by-Step Practice
1. Clone Repository and Install Dependencies
git clone https://github.com/jaeneungsim/cdk-cognito-user-pool-pattern.git
cd cdk-cognito-user-pool-pattern
npm install
2. Configure AWS Profile
export AWS_PROFILE=your-profile-name
3. CDK Bootstrap (First time only)
cdk bootstrap
4. Deploy All Stacks
cdk deploy --all
5. Note the Deployment Outputs After deployment completes, save these values:
- CognitoStack.UserPoolId
- CognitoStack.UserPoolClientId
- CognitoStack.IdentityPoolId
- FrontendStack.WebsiteURL
6. Test Web Application
Visit the deployed CloudFront URL: https://your-cloudfront-domain.cloudfront.net
In the web interface:
- Enter Region: Your deployed AWS region (e.g., ap-southeast-2)
- Enter User Pool ID: Copy from deployment outputs
- Enter Client ID: Copy from deployment outputs
- Enter Identity Pool ID: Copy from deployment outputs
- Click "Initialize Cognito"
- Fill Registration Form: Select seller or buyer group
- Email Verification: Enter the verification code received
- Test Login
- Click "Get AWS Credentials": View temporary AWS credentials
7. Direct API Testing
Test actual permissions using AWS SDK:
// Seller group - S3 write permission test (product image upload)
const s3 = new AWS.S3({
accessKeyId: credentials.AccessKeyId,
secretAccessKey: credentials.SecretAccessKey,
sessionToken: credentials.SessionToken
});
try {
await s3.putObject({
Bucket: 'product-images',
Key: 'seller-product-001.jpg',
Body: imageData
}).promise();
console.log('✅ Seller permissions: Product image upload successful');
} catch (error) {
console.log('❌ Access denied:', error.message);
}
8. Monitoring Verification
Check these metrics in CloudWatch:
- Cognito User Pool user registration count
- Cognito Identity Pool credential issuance count
- IAM role AssumeRole call count
- Lambda execution time and error rates
9. Resource Cleanup
cdk destroy --all
Advantages and Considerations
Advantages
1. Strong Security
- Fine-grained permission control through AWS IAM policies
- Minimize security risks with temporary credentials
- Permission verification at AWS service level
2. Scalability
- Consistent performance regardless of user count
- Simple group addition/modification
- Unlimited scalability of AWS managed services
3. Development Productivity
- Separate authentication logic from application
- Direct AWS SDK usage possible
- No complex permission check logic needed
4. Cost Efficiency
- Cognito usage-based billing
- Reduced Lambda execution time
- Lower infrastructure operational costs
Considerations
1. Initial Setup Complexity
- Configure Cognito User Pool, Identity Pool, IAM policies
- Set up group-to-permission mappings
- Frontend SDK integration
2. Debugging Complexity
- Multiple AWS service interactions
- Difficult IAM policy troubleshooting
- Need CloudTrail for log tracking
3. Permission Change Immediacy Limitations
- Must wait until temporary credentials expire (up to 12 hours)
- Difficult real-time permission changes
- Need additional mechanisms for emergency permission blocking
Scalability and Real-World Applications
Feature Extensions
Based on this foundational pattern, you can add features like:
Database Integration
// DynamoDB table-level access permissions
{
"Effect": "Allow",
"Action": ["dynamodb:GetItem", "dynamodb:Query"],
"Resource": "arn:aws:dynamodb:*:*:table/UserData-*",
"Condition": {
"ForAnyValue:StringEquals": {
"cognito:groups": ["seller"]
}
}
}
File Upload System
// Group-based S3 path separation
{
"Effect": "Allow",
"Action": ["s3:PutObject"],
"Resource": "arn:aws:s3:::uploads/seller/${cognito-identity.amazonaws.com:sub}/*",
"Condition": {
"ForAnyValue:StringEquals": {
"cognito:groups": ["seller"]
}
}
}
Notification System
// Group-based SNS topic subscription
{
"Effect": "Allow",
"Action": ["sns:Subscribe"],
"Resource": "arn:aws:sns:*:*:seller-notifications",
"Condition": {
"ForAnyValue:StringEquals": {
"cognito:groups": ["seller"]
}
}
}
API Gateway Integration
When you enable AWS_IAM authentication on API Gateway endpoints, API calls are automatically controlled based on the permissions of the user's temporary credentials. For example, you can restrict product registration APIs to only be callable by users in the seller group through IAM policies.
Production Implementation Considerations
Security Hardening
- Enable MFA (Multi-Factor Authentication)
- Strengthen password policies
- Set account lockout policies
- Enable CloudTrail audit logs
Monitoring and Alerts
- Configure CloudWatch dashboards
- Alert on abnormal login attempts
- Detect privilege escalation
- Notify on credential expiration
Backup and Disaster Recovery
- Backup Cognito User Pool
- Version control IAM policies
- Consider multi-region replication
Conclusion
The group-based authentication system using AWS Cognito User Pools and Identity Pools is a powerful pattern that achieves both "secure and scalable user permission management."
This pattern is particularly useful when:
- User roles are clearly defined and
- Direct access to AWS resources is required and
- Both security and scalability are important
Of course, it's not suitable for every situation. For real-time permission changes or simple applications, it might only add unnecessary complexity.
However, for modern serverless applications where business logic is complex and both user experience and system reliability are important, I believe this is a very valuable pattern.
If your next project has such requirements, I encourage you to consider this Cognito pattern!