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.
sequenceDiagram participant Browser as "Browser" participant UP as "Cognito User Pool" participant APIGW as "API Gateway" participant Lambda as "Lambda Function" Browser->>UP: "Sign In (email + password)" UP-->>Browser: "ID Token + Access Token + Refresh Token" Browser->>APIGW: "GET /api/data (Authorization: Bearer AccessToken)" APIGW->>UP: "Validate Access Token" UP-->>APIGW: "Token valid, user claims" APIGW->>Lambda: "Forward request with user context" Lambda-->>Browser: "200 OK — Response Data"
  1. User submits credentials (email + password) from the browser.
  2. Cognito User Pool validates credentials against its internal user directory.
  3. On success, Cognito returns ID Token, Access Token, and Refresh Token (JWTs).
  4. The browser sends the Access Token in the Authorization header to your API Gateway.
  5. API Gateway uses a Cognito Authorizer to validate the token and route the request to your Lambda function.
  6. 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.

sequenceDiagram participant Client as "Web Client" participant UP as "Cognito User Pool" participant IP as "Cognito Identity Pool" participant STS as "AWS STS" participant S3 as "Amazon S3" Client->>UP: "Sign In" UP-->>Client: "ID Token (JWT)" Client->>IP: "Exchange ID Token for AWS credentials" IP->>STS: "AssumeRoleWithWebIdentity" STS-->>IP: "Temporary AWS Credentials" IP-->>Client: "AccessKeyId + SecretKey + SessionToken" Client->>S3: "PutObject (using temp credentials)" S3-->>Client: "Upload successful"
  1. User authenticates via Cognito User Pool and receives a JWT (ID Token).
  2. The client presents the ID Token to the Cognito Identity Pool.
  3. The Identity Pool calls AWS STS to assume a mapped IAM Role.
  4. STS returns temporary credentials (Access Key ID, Secret Access Key, Session Token).
  5. 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.

graph TD User["👤 Web App User"] UP["Cognito User Pool
(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.

Glossary

TermDefinition
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 IdentityAllowing users authenticated by an external IdP (Google, SAML) to access your system without creating a separate password.

Comments

Popular posts from this blog

EC2 No Internet Access in Custom VPC: Attaching an Internet Gateway and Fixing Route Tables

IAM User vs. IAM Role: Why Your EC2 Instance Should Never Use a User

Lambda Infinite Loop with S3: How to Prevent Recursive Triggers