AWS Cognito Demystified: User Pools vs. Identity Pools — Which One Manages Your User Database?
You're building a web app and need to add Sign Up and Sign In. You open the AWS Cognito console and immediately face a fork in the road: User Pools or Identity Pools? Choosing the wrong one wastes days of refactoring. This post gives you the definitive answer.
TL;DR — The One-Line Answer
If you need a user database with login (Sign Up, Sign In, passwords, MFA), use a User Pool. Identity Pools are for granting AWS resource access (S3, DynamoDB) to users — they are not a user directory.
| Feature | User Pool | Identity Pool |
|---|---|---|
| Primary Purpose | Authentication (who are you?) | Authorization (what can you access?) |
| Stores User Profiles? | ✅ Yes — username, email, attributes | ❌ No — no user directory |
| Sign Up / Sign In UI | ✅ Hosted UI available | ❌ Not applicable |
| Issues Tokens? | ✅ JWT (ID, Access, Refresh tokens) | ✅ Temporary AWS credentials (STS) |
| MFA / Password Policy | ✅ Fully configurable | ❌ Not applicable |
| Social Login (Google, Facebook) | ✅ Via federated IdPs | ✅ Via external IdPs or User Pool token |
| Direct AWS SDK Access | ❌ Not directly | ✅ Core purpose |
| Typical Use Case | App login, user management | Upload to S3, query DynamoDB from client |
Mental Model: The Airport Analogy
User Pool = Passport Control. It verifies your identity, stamps your passport (JWT token), and confirms you are who you claim to be.
Identity Pool = Airport Lounge Access Desk. It takes your stamped passport and decides which lounges (AWS services) you're allowed to enter, issuing a temporary access badge (STS credentials).
Deep Dive: How Each Service Works
1. Cognito User Pool — Your User Directory
A User Pool is a fully managed OpenID Connect (OIDC) identity provider. When a user signs up, Cognito stores their profile (email, phone, custom attributes) and manages credentials securely. On successful sign-in, it returns three JWTs:
- ID Token: Contains user identity claims (email, sub, custom attributes). Use this to identify the user in your backend.
- Access Token: Used to authorize calls to your API (e.g., via API Gateway Cognito Authorizer). Contains scopes and groups.
- Refresh Token: Long-lived token used to obtain new ID/Access tokens without re-login.
- User submits credentials (email + password) from the browser.
- Cognito User Pool validates credentials against its internal user directory.
- On success, Cognito returns ID Token, Access Token, and Refresh Token (JWTs).
- The browser sends the Access Token in the Authorization header to your API Gateway.
- API Gateway uses a Cognito Authorizer to validate the token and route the request to your Lambda function.
- Lambda processes the request and returns a response.
2. Cognito Identity Pool — AWS Credential Vending Machine
An Identity Pool does not store users. It accepts a trusted token (from a User Pool, Google, Facebook, or SAML IdP) and exchanges it for temporary AWS credentials via AWS STS. These credentials are scoped to an IAM role you define.
- User authenticates via Cognito User Pool and receives a JWT (ID Token).
- The client presents the ID Token to the Cognito Identity Pool.
- The Identity Pool calls AWS STS to assume a mapped IAM Role.
- STS returns temporary credentials (Access Key ID, Secret Access Key, Session Token).
- The client uses these credentials to directly access AWS services (e.g., upload a file to S3).
The Combined Architecture (Most Production Apps)
In most real-world applications, you use both: User Pool for authentication, Identity Pool for granting fine-grained AWS resource access to authenticated users.
(Authentication / User DB)"] IP["Cognito Identity Pool
(AWS Credential Vending)"] APIGW["API Gateway
(Cognito Authorizer)"] Lambda["Lambda Function"] STS["AWS STS"] S3["Amazon S3"] DDB["Amazon DynamoDB"] User -->|"Sign Up / Sign In"| UP UP -->|"JWT Tokens"| User User -->|"Access Token"| APIGW APIGW -->|"Authorized Request"| Lambda User -->|"ID Token"| IP IP -->|"AssumeRole"| STS STS -->|"Temp AWS Credentials"| User User -->|"Direct SDK Call"| S3 User -->|"Direct SDK Call"| DDB style UP fill:#FF9900,color:#fff,stroke:#FF9900 style IP fill:#1A73E8,color:#fff,stroke:#1A73E8 style APIGW fill:#2E7D32,color:#fff,stroke:#2E7D32
Implementation: Setting Up a User Pool for Sign Up & Sign In
Below is a practical AWS CLI walkthrough to create a User Pool and App Client — the minimum required for web app authentication.
Step 1: Create the User Pool
# Create a User Pool with email-based sign-in and strong password policy
aws cognito-idp create-user-pool \
--pool-name "MyAppUserPool" \
--policies '{
"PasswordPolicy": {
"MinimumLength": 8,
"RequireUppercase": true,
"RequireLowercase": true,
"RequireNumbers": true,
"RequireSymbols": false
}
}' \
--auto-verified-attributes email \
--username-attributes email \
--mfa-configuration OFF \
--region us-east-1
Step 2: Create an App Client (No Secret for Browser Apps)
# Replace USER_POOL_ID with the ID returned from Step 1
aws cognito-idp create-user-pool-client \
--user-pool-id us-east-1_XXXXXXXXX \
--client-name "MyWebAppClient" \
--no-generate-secret \
--explicit-auth-flows ALLOW_USER_SRP_AUTH ALLOW_REFRESH_TOKEN_AUTH \
--region us-east-1
Why --no-generate-secret? Browser-based apps cannot securely store a client secret. Use the secret only for server-side or machine-to-machine flows.
Step 3: Integrate with AWS Amplify (Frontend)
🔽 Click to expand — Amplify Auth configuration (JavaScript)
// Install: npm install aws-amplify
import { Amplify } from 'aws-amplify';
import { signUp, signIn, signOut, getCurrentUser } from 'aws-amplify/auth';
// Configure Amplify with your User Pool details
Amplify.configure({
Auth: {
Cognito: {
userPoolId: 'us-east-1_XXXXXXXXX', // From Step 1
userPoolClientId: 'YYYYYYYYYYYYYYYYYY', // From Step 2
loginWith: {
email: true
}
}
}
});
// --- Sign Up ---
async function handleSignUp(email, password) {
try {
const { isSignUpComplete, userId } = await signUp({
username: email,
password,
options: {
userAttributes: { email }
}
});
console.log('Sign up successful. User ID:', userId);
// Next step: prompt user to verify email (confirmation code)
} catch (error) {
console.error('Sign up error:', error);
}
}
// --- Sign In ---
async function handleSignIn(email, password) {
try {
const { isSignedIn, nextStep } = await signIn({ username: email, password });
if (isSignedIn) {
console.log('User signed in successfully.');
// Tokens are managed automatically by Amplify
}
} catch (error) {
console.error('Sign in error:', error);
}
}
// --- Get Current Authenticated User ---
async function getUser() {
try {
const user = await getCurrentUser();
console.log('Current user:', user.username);
} catch {
console.log('No user signed in.');
}
}
Step 4: Protect Your API with API Gateway + Cognito Authorizer
🔽 Click to expand — API Gateway Cognito Authorizer (CloudFormation snippet)
Resources:
MyApiAuthorizer:
Type: AWS::ApiGateway::Authorizer
Properties:
Name: CognitoUserPoolAuthorizer
Type: COGNITO_USER_POOLS
RestApiId: !Ref MyRestApi
IdentitySource: method.request.header.Authorization
ProviderARNs:
- !Sub "arn:aws:cognito-idp:us-east-1:123456789012:userpool/us-east-1_XXXXXXXXX"
IAM Considerations for Identity Pools
When using an Identity Pool, you must define at least two IAM roles: one for authenticated users and one for unauthenticated (guest) users. Apply least privilege — grant only the specific S3 prefix or DynamoDB table the user needs.
🔽 Click to expand — Least-privilege IAM policy for authenticated Identity Pool users
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:PutObject",
"s3:GetObject"
],
"Resource": "arn:aws:s3:::my-app-uploads/${cognito-identity.amazonaws.com:sub}/*"
}
]
}
The ${cognito-identity.amazonaws.com:sub} policy variable scopes each user's access to their own S3 prefix — a critical security pattern.
Decision Framework: Which Should You Use?
| Your Requirement | Use This |
|---|---|
| Sign Up, Sign In, user profiles, MFA | User Pool |
| Protect a REST API (API Gateway) | User Pool (Cognito Authorizer) |
| Social login (Google, Apple, Facebook) | User Pool (federated IdPs) |
| Client-side S3 upload / DynamoDB access | Identity Pool (backed by User Pool) |
| Guest / unauthenticated access to AWS resources | Identity Pool (unauthenticated role) |
| Full production app (auth + AWS resource access) | User Pool + Identity Pool |
Wrap-Up & Next Steps
For your web app's Sign Up and Sign In: start with a Cognito User Pool. It is your user database, authentication engine, and token issuer. Add an Identity Pool only when your frontend needs direct AWS service access. Use AWS Amplify to abstract the SDK complexity in your frontend code.
- 📖 Official Docs: Cognito User Pools
- 📖 Official Docs: Cognito Identity Pools
- 📖 AWS Amplify Auth Documentation
Glossary
| Term | Definition |
|---|---|
| JWT (JSON Web Token) | A signed, self-contained token encoding user claims. Cognito User Pools issue JWTs on successful authentication. |
| STS (Security Token Service) | AWS service that issues temporary, scoped credentials. Used by Identity Pools to grant AWS resource access. |
| IdP (Identity Provider) | A system that authenticates users. Cognito User Pool itself is an IdP; it can also federate external IdPs like Google. |
| SRP (Secure Remote Password) | A cryptographic protocol for password authentication that avoids sending the password over the network. Used by Cognito's ALLOW_USER_SRP_AUTH flow. |
| Federated Identity | Allowing users authenticated by an external IdP (Google, SAML) to access your system without creating a separate password. |
Comments
Post a Comment