CertNote/ Commute Study Notes
Roadmap
KOEN
CLF-C02 · FoundationalCloud Practitioner - Foundational
DVA-C02 · AssociateDeveloper - Associate
  • Week 1
    • 1.AWS from a Developer's Perspective: Regions, Infrastructure, and the Boundary of Responsibility
    • 2.The 4 Core Entities of IAM: User, Group, Role, Policy
    • 3.STS and Policy Conditions: The Deep World of Temporary Credentials
    • 4.AWS CLI and SDK: From Credential Chaining to SigV4
    • 5.Week 1 Review: The Chain of Trust Built by Infrastructure and IAM
  • Week 2
  • Week 3
  • Week 4
  • Week 5
  • Week 6
  • Week 7
  • Week 8
  • Week 9
    • 1.KMS: Handling Encryption Keys Without Touching Them Directly
    • 2.Secrets Manager and Parameter Store: Getting Secrets Out of Code
    • 3.Cognito: Why Authentication and Authorization Split Into Two Pools
    • 4.WAF, Shield, ACM: Three Layers to Filter Traffic Before It Touches the App
    • 5.Week 9 Review: Security Is Not a Single Setting but the Sum of Layers
  • Week 10
  • Week 11
  • Week 12
  • Week 13
SAA-C03 · AssociateSolutions Architect - Associate
SOA-C02 · AssociateCloudOps Engineer - Associate
SAP-C02 · ProfessionalSolutions Architect - Professional
DOP-C02 · ProfessionalDevOps Engineer - Professional
Linux Master 2 · 2급KAIT Linux Master - Level 2
SCS-C03 · SpecialtySecurity - Specialty
Linux Master 1 · 1급KAIT Linux Master - Level 1
MLA-C01 · AssociateMachine Learning Engineer - Associate
AIF-C01 · FoundationalAI Practitioner - Foundational
DEA-C01 · AssociateData Engineer - Associate
MLS-C01 · SpecialtyMachine Learning - Specialty
SQLD · 개발자SQL Developer (SQLD)

© CertNote · 주간랩스

  • About
  • Changelog
  • Contact
  • Terms
  • Privacy
← DVA-C02/Week 9/Day 3

Based on the DVA-C02 exam guide·Report an error

DVA-C02· AssociateWeek 9 · Day 3~24 min read

Day 3 - Cognito: Why Authentication and Authorization Split Into Two Pools

Almost every developer building web and mobile apps hits the same sequence of mistakes: first hardcoding passwords plaintext in user tables, learning hashing and using bcrypt, then implementing password reset, email verification, MFA, social login, token expiry, refresh-token rotation — eventually asking "why am I building all this?" Authentication and authorization are security's most fragile domains; homegrown systems almost always have gaps somewhere. Amazon Cognito manages this entire "don't build it yourself" realm as a service.

Cognito is tricky in DVA-C02 because two separate components (User Pool and Identity Pool) coexist with confusing names. They solve completely different problems — User Pool "handles login and issues identity JWT," while Identity Pool "swaps that identity JWT for AWS resource access credentials (IAM temporary credentials)." Understanding this split solves most Cognito questions. This article explores why they separated, what each of the three JWT types does, and how Lambda triggers extend the auth flow.

Authentication and Authorization Are Different Problems, So Two Pools

OAuth and OpenID Connect study hits a core concept: authentication and authorization differ. Authentication is "is this person really user@example.com?" Authorization is "can this person write to S3?" Issuing a passport (authentication) and issuing a visa (authorization) are different acts.

Cognito split these two into separate services.

AspectUser PoolIdentity Pool
SolvesAuthentication (who are you?)Authorization (what can you access?)
Inputusername/password, social loginUser Pool JWT (or external IdP token)
OutputJWT tokens (3 types)IAM temporary credentials (STS)
Used ForAPI Gateway auth, user directoryS3·DynamoDB SDK direct access
GuestNoYes (unauthenticated role)

💡 Related theory: This reflects the structure of the OAuth 2.0 / OIDC specs. OIDC says "ID Token proves identity"; OAuth 2.0 says "Access Token authorizes resource access" — they're separate specs because they're separate concerns. User Pool acts as OIDC IdP (issuer of identity), Identity Pool as token-to-AWS-credential exchanger. AWS physically split them so "JWT-only apps" use User Pool alone, while "direct AWS resource access" apps add Identity Pool. This design lets each component do one thing well.

⚠️ Trap: The most frequent exam trap swaps these two. "Mobile app uploads files to S3 directly" needs Identity Pool (JWT → IAM credential → S3 direct access). User Pool JWT alone cannot call S3 SDK — JWT is an ID card, not AWS credentials. Conversely, "user login/signup + API Gateway token validation" is User Pool.

Three JWTs: ID Card, Access Ticket, Refresh Right

User Pool login success returns three token types. Distinguishing them is frequent on exams.

TokenContentsPurposeDefault Expiry
ID TokenUser identity (sub, email, name, groups)"Prove this person's identity"1 hour
Access TokenOAuth scope, user identifier"What actions are authorized?"1 hour
Refresh Token(opaque)Renew ID/Access tokens30 days (configurable 1 day–10 years)
// ID Token payload (decoded)
{
  "sub": "a1b2c3d4-...",
  "email": "user@example.com",
  "cognito:groups": ["Admin"],
  "iss": "https://cognito-idp.ap-northeast-2.amazonaws.com/<poolId>",
  "aud": "<appClientId>",
  "token_use": "id",
  "exp": 1721000000
}

🔍 Going deeper: JWT (JSON Web Token) is Header.Payload.Signature — three Base64URL parts joined by dots. Header and payload are just encoding, not encryption — anyone can decode and read. Security comes from Signature. Cognito signs with RS256 (RSA asymmetric) using a private key, and verifiers get the public key from Cognito's published JWKS (/.well-known/jwks.json) to verify. Asymmetry means verifiers need only the public key; Cognito holds the private key alone. So "can I put a password in JWT?" is a firm No — payload is plaintext-readable. Putting sensitive data there exposes it. The token_use claim ("id" vs "access") matters for validation.

⚠️ Trap: API Gateway's Cognito Authorizer defaults to ID Token, while HTTP API's JWT Authorizer typically validates Access Token (scope-aware). This token-type difference is exam gold. REST API + Cognito Authorizer = ID Token; HTTP API + JWT Authorizer = Access Token. Token mismatch fails validation.

User Pool Auth Flow: SRP Doesn't Send Passwords Over Network

User Pool supports multiple auth flows; the recommended USER_SRP_AUTH never transmits the password itself.

FlowBehavior
USER_SRP_AUTH(recommended)SRP protocol — password never sent over network
USER_PASSWORD_AUTHPassword sent directly (TLS required), legacy-migration only
ADMIN_USER_PASSWORD_AUTHBackend (admin-privileged) auth
REFRESH_TOKEN_AUTHRefresh token to renew ID/Access tokens
CUSTOM_AUTHLambda trigger defines custom challenge

💡 Related theory: SRP (Secure Remote Password) is a PAKE (Password-Authenticated Key Exchange) from 1998 Stanford. The core idea: client and server prove they share a password without ever exchanging it — based on discrete-log math. Server stores only a verifier, not the password itself — even if server DB is breached, passwords don't leak directly. TLS already encrypts transmission, but SRP defends even if the TLS termination point (load balancer, proxy) momentarily sees plaintext. Defense in depth: multiple independent layers.

Lambda Triggers: Inject Code Into the Auth Lifecycle

Cognito's real flexibility comes from embedding Lambda at key points in authentication. Eleven trigger types exist; exam asks "which trigger at which point?"

TriggerWhenCommon Use
PreSignUpBefore signup completesEmail domain validation, auto-approve
PostConfirmationAfter email/SMS confirmationAdd user to app DB, welcome email
PreAuthenticationAt login attemptBlocklist check
PostAuthenticationAfter login successAudit log, record last login
PreTokenGenerationBefore JWT generationAdd custom claims, inject groups
DefineAuthChallenge etcCustom auth flowOTP, CAPTCHA challenges
UserMigrationAt login/password resetGradual migration from legacy IdP
CustomMessageBefore message sendCustomize email/SMS text
# PreSignUp trigger — corporate-email-only signup + auto-approve
def lambda_handler(event, context):
    email = event['request']['userAttributes'].get('email', '')
    if not email.endswith('@mycompany.com'):
        raise Exception("Company email only.")
    # Auto-approve corporate domain without email verification
    event['response']['autoConfirmUser'] = True
    event['response']['autoVerifyEmail'] = True
    return event   # Must return event for flow to continue

🔍 Going deeper: UserMigration trigger enables seamless migration from legacy auth to Cognito without downtime. Instead of bulk-importing all users, on first Cognito login attempt, Cognito calls UserMigration Lambda. Lambda tries authenticating with the old system; if successful, instantly creates that user in User Pool. Users self-migrate one login at a time — no mass password resets, no big-bang cutover. Old system slowly empties as users log in. This "usage-driven gradual migration" beats "cutover migration" every time.

📚 Case study: A team put all user permissions in ID Token claims, making the JWT 8KB+. Some browsers and proxies enforce 8KB header-size limits — requests started failing with 401 intermittently. JWTs travel in headers per-request, so claims must stay minimal — IDs and groups, with heavy permission data looked up server-side. Tokens are ID cards, not permission databases.

Identity Pool: Converting JWT to AWS Credentials in Two Steps

Even with User Pool JWT, you cannot call S3 or DynamoDB SDK — AWS SDK requires IAM credentials. Identity Pool bridges this in two steps.

import boto3
ci = boto3.client('cognito-identity')
 
# 1) GetId — present JWT, receive Identity ID
logins = {'cognito-idp.ap-northeast-2.amazonaws.com/<poolId>': id_token}
identity_id = ci.get_id(IdentityPoolId='ap-northeast-2:<poolId>', Logins=logins)['IdentityId']
 
# 2) GetCredentialsForIdentity — get STS temporary credentials
creds = ci.get_credentials_for_identity(IdentityId=identity_id, Logins=logins)['Credentials']
 
# Now call S3 directly with IAM credentials
s3 = boto3.client('s3',
    aws_access_key_id=creds['AccessKeyId'],
    aws_secret_access_key=creds['SecretKey'],
    aws_session_token=creds['SessionToken'])

🔍 Going deeper: Internally, Identity Pool credentials issue via STS AssumeRoleWithWebIdentity. Present a web identity token (JWT), STS validates it and assumes an IAM role mapped to Identity Pool, returning temporary credentials. The key is role mapping — all authenticated users get the same role (Default), or JWT claims split them across different roles (Rules-based or cognito:preferred_role Token-based). For example, Admin group → AdminRole, regular users → UserRole, letting you split S3 prefix or DynamoDB access per group within the same app. This is how "group info in JWT becomes IAM permissions."

Guest (unauthenticated) access is Identity Pool-only. Issue unauth-role creds to unauthenticated users for limited read-only S3 access, etc.

External IdP Federation

User Pool can host its own user directory, but it can also federate external IdPs (Google, Facebook, Apple, SAML 2.0, OIDC). When a user logs in via Google, that identity mirrors into User Pool (just-in-time provisioning), and the app receives a normal Cognito JWT — the app sees one consistent token interface regardless of login source.

💡 Related theory: This "unified token interface across multiple IdPs" is the essence of federation. Without federation, apps handle Google, Apple, and custom login each differently — code explodes by N. With User Pool as broker, the app handles one token type. You can mix SAML (enterprise SSO) and OIDC (social) in one pool, supporting B2B and B2C simultaneously — User Pool abstracts the identity source.

Wrapping Up

Cognito splits into two because authentication and authorization are different. User Pool handles login and issues three JWT types (identity, access, refresh), while Identity Pool exchanges JWT for STS temporary credentials for AWS resource access. JWT is Base64-encoded plaintext (no encryption), so never put secrets in it. Eleven Lambda triggers extend the auth lifecycle. Identity Pool role mapping turns JWT claims into IAM permissions — this architecture is exam trap bedrock.

Next we look at the defense layer before requests reach the app — WAF, Shield, ACM — which guard traffic at the edge.

📝 Practice Questions

Click a choice to reveal the answer and explanation.

Question 1

Mobile app user logs in via Cognito, then uploads photos to their S3 folder directly. Correct flow?

Question 2

API Gateway REST API with Cognito Authorizer requires which token type?

Question 3

Signup must accept only @mycompany.com email and auto-approve. Which trigger?

Question 4

Can you put passwords or other sensitive data in JWT ID Token?

Question 5

Migrate legacy auth users to Cognito without downtime. Best method?

Question 6

Identity Pool admin role, regular users role split. Implementation?

Question 7

User Pool supports Google, Apple, self login. What token does the app receive?

PreviousSecrets Manager and Parameter Store: Getting Secrets Out of CodeWeek 9 · Day 2Next WAF, Shield, ACM: Three Layers to Filter Traffic Before It Touches the AppWeek 9 · Day 4

Official references

  • DVA-C02 official exam page(opens in a new tab)
  • Amazon Cognito documentation(opens in a new tab)
  • Amazon S3 documentation(opens in a new tab)
  • AWS Lambda documentation(opens in a new tab)
  • AWS IAM documentation(opens in a new tab)
  • Amazon API Gateway documentation(opens in a new tab)

On this page

  • Authentication and Authorization Are Different Problems, So Two Pools
  • Three JWTs: ID Card, Access Ticket, Refresh Right
  • User Pool Auth Flow: SRP Doesn't Send Passwords Over Network
  • Lambda Triggers: Inject Code Into the Auth Lifecycle
  • Identity Pool: Converting JWT to AWS Credentials in Two Steps
  • External IdP Federation
  • Wrapping Up
  • Practice Questions