CertNote/ Commute Study Notes
Roadmap
KOEN
CLF-C02 · FoundationalCloud Practitioner - Foundational
DVA-C02 · AssociateDeveloper - Associate
SAA-C03 · AssociateSolutions Architect - Associate
  • Week 1
    • 1.The Map of AWS Infrastructure: Regions, AZs, and the Promise Called Shared Responsibility
    • 2.IAM Fundamentals: Who Gets to Do What
    • 3.IAM Deep Dive: STS, Permissions Boundaries, and the Shadow of the Confused Deputy
    • 4.AWS Organizations, SCP, Control Tower: The Skeleton of Multi-Account Governance
    • 5.Week 1 Review: Fundamentals and IAM, Hardened Through Scenarios
  • Week 2
  • Week 3
  • Week 4
  • Week 5
  • Week 6
  • Week 7
  • Week 8
    • 1.KMS: Why Key Management Is the Root of Cloud Security
    • 2.Secrets Manager, Parameter Store, and CloudHSM: Three Tools for Handling Secrets and Configuration
    • 3.Cognito: Peeling User Authentication Off Into a Cloud Service
    • 4.WAF, Shield, GuardDuty, Inspector, Macie: The 5 Pillars of Cloud Security Operations
    • 5.Week 8 Wrap-Up: 12 Security Domain Scenarios
  • Week 9
  • Week 10
  • Week 11
  • Week 12
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
← SAA-C03/Week 8/Day 3

Based on the SAA-C03 exam guide·Report an error

SAA-C03· AssociateWeek 8 · Day 3~38 min read

Day 3 - Cognito: Peeling User Authentication Off Into a Cloud Service

Any developer who has built their own membership system knows the feeling: authentication and authorization look simple at first, then slowly grow into a monster. In the beginning it seems like "email + bcrypt password + JWT" is all you need, but soon the demands pile up one after another — password policies, password-reset emails, MFA, social login (Google, Facebook, Apple), enterprise SSO (SAML, Okta, AD), device trust, threat detection, login attempt throttling, token refresh, refresh-token rotation, GDPR consent management, session expiry. The result is that your homegrown auth code ends up more complex than your payment system, and over 70% of security incidents blow up right here.

The category that split this problem out into SaaS is identity-as-a-service, and companies/services like Auth0 (2013), Okta (2009), and Firebase Auth (2016) built this market. AWS started Cognito in July 2014 as part of the mobile SDK, then in July 2016 locked in its current shape — the split between User Pool (a user directory) and Identity Pool (issuing AWS credentials). The division of labor between the two is the single biggest trap on both the exam and the job, and you can only solve SAA's security-domain authn/authz scenarios if you're precise about the difference between "cases that use only one of them" and "cases that combine both."

User Pool vs Identity Pool: Two Pools Answering Different Questions

The first thing that confuses people when they meet Cognito is "why are there two pools?" — and it's because the two pools answer completely different questions.

  • User Pool deals with "who is this user?" It handles sign-up, sign-in, password policy, MFA, email/SMS verification, and external IdP federation, and on success it issues three kinds of JWTs (JSON Web Tokens) — ID Token, Access Token, Refresh Token. These tokens are used by your backend — API Gateway, AppSync, ALB — to identify the user.
  • Identity Pool (Federated Identities) deals with "what AWS permissions do we temporarily grant this user?" It takes a User Pool JWT or a token from another IdP (Google, Facebook, Apple, SAML), calls STS (AssumeRoleWithWebIdentity), and hands the user temporary credentials for an IAM Role. That lets a mobile app call AWS APIs directly — S3 uploads, DynamoDB writes, and so on.

If you boil the difference down to one sentence: "User Pool mints JWTs for your API; Identity Pool mints temporary credentials for AWS APIs."

DimensionUser PoolIdentity Pool
OutputJWT (ID/Access/Refresh)STS temporary credentials (AccessKey/SecretKey/SessionToken)
Called byAPI Gateway / AppSync / ALB / your backendAWS APIs (S3, DynamoDB, Kinesis, etc.)
User storageHas its own directoryNone (maps identities from an external IdP)
External IdPSAML, OIDC, Google, FacebookCognito User Pool, Google, Facebook, Apple, SAML, OIDC, Developer Authenticated Identities
MFASupportedNo MFA of its own
PricingMAU-based (first 50,000 MAU/month free)Nearly free (charged only for STS calls)
[ The most common combined pattern ]

Mobile App
   │ 1) Sign in via Hosted UI or SDK
   ▼
User Pool ── (optional) external IdP federation (Google / SAML / Okta)
   │ JWT (ID/Access/Refresh)
   │
   ├─ 2-A) Pass JWT to API Gateway → JWT Authorizer verifies → invoke Lambda
   │
   └─ 2-B) Hand JWT to Identity Pool
              │ AssumeRoleWithWebIdentity
              ▼
           STS → temporary credentials
              │
              ▼
           Direct S3 PUT / direct DynamoDB write

A common mistake is "trying to do direct S3 uploads with User Pool alone." A JWT is a credential that's only valid against the API you built, and AWS's S3 API requires a SigV4 signature — so you can't just carry a JWT and call it. For a mobile client to call S3 directly, it must go through an Identity Pool to get temporary IAM credentials. Conversely, "authenticating your backend with Identity Pool alone" is also impossible. Identity Pool has no user directory of its own, so there's no password policy, no MFA, no email verification.

💡 Related theory: This division of labor follows OAuth2's separation between the "authorization server" (which issues who-you-are) and "STS" (which temporarily delegates permissions). User Pool plays the OAuth2/OIDC authorization-server role, while Identity Pool plays a role similar to STS in the SAML world. Merge the two into one and you get something as simple as Auth0 or Firebase Auth — but you lose AWS's core value proposition: "fine-grained permission granting to AWS resources at the IAM Role level."

🔍 Going deeper: The STS API that a Cognito Identity Pool calls is AssumeRoleWithWebIdentity, and this is actually not Cognito-specific — it's an OIDC standard. AWS lets external systems like GitHub Actions, GitLab CI, and Kubernetes Pods (IRSA) use the same API, generalizing the pattern of "trust a JWT from an external OIDC IdP and grant an IAM Role." A Cognito Identity Pool is closer to a wrapper that packages this pattern behind a mobile/web SDK.

The Three Tokens and the Traps of Token Verification

The roles of the three tokens a User Pool issues come up most often on the exam.

TokenFormatPurposeDefault lifetime
ID TokenJWT (signed)"Who the user is" (name, email, custom attributes)1 hour (tunable 5 min – 24 hours)
Access TokenJWT (signed)"What permissions they hold" (scope, group)1 hour
Refresh TokenOpaque stringReissue ID/Access Tokens30 days default (1 hour – 10 years)

The ID Token carries the user's identity (claims) and is used by the backend to identify "whose request is this." The Access Token is the OAuth2-standard token and carries permission scopes and groups (cognito:groups). API Gateway's JWT Authorizer can accept either, but the Access Token is the standard choice. The Refresh Token is the token used to obtain fresh tokens once they expire, and the client must store it securely (Keychain/Keystore on mobile, an HttpOnly Secure Cookie on the web are recommended).

Because a JWT is a token the client carries around, it has the property that "it can't be changed after issuance." That is, even if a user loses permissions or changes their password, an already-issued JWT stays valid until it expires. This is the biggest trap of JWTs, and Cognito offers two remedies. ① Set short token lifetimes (e.g., 5–15 minutes) with frequent refresh. ② Use the GlobalSignOut API to immediately invalidate all of a user's refresh tokens. Note that GlobalSignOut cannot invalidate already-issued ID/Access Tokens — so if you truly need instant cutoff, you need an extra layer: a token blocklist at the backend.

[ JWT verification flow ]

API Gateway / backend
   │ Authorization: Bearer eyJraWQ...
   │
   ▼
1) Extract kid from the header
2) Call the Cognito JWKS endpoint (fetch once + cache)
   https://cognito-idp.<region>.amazonaws.com/<userPoolId>/.well-known/jwks.json
3) Look up the public key matching the kid
4) Verify the JWT signature (RS256)
5) Check claims:
   - iss = https://cognito-idp.<region>.amazonaws.com/<userPoolId>
   - exp > current time
   - aud = your app's App Client ID
   - token_use = "id" or "access"

Don't call the JWKS endpoint on every request (latency + Cognito limits). The standard is "fetch the JWKS once, cache it in memory, and re-fetch only on a kid mismatch." AWS almost never changes the JWKS, but key rotation can happen, so the recommended pattern is "if you can't find the kid, re-fetch once, and if you still can't find it, error out."

⚠️ Pitfall: When you see the keyword "JWT verification at API Gateway" on the exam, the answer is a Cognito User Pool Authorizer for REST APIs (REST-only) and a JWT Authorizer for HTTP APIs (general OIDC). The HTTP API's JWT Authorizer can also accept other OIDC IdPs beyond Cognito (Auth0, Okta), making it more flexible, but features like Cognito group-based routing work a bit differently. Don't confuse the two API types.

External IdP Federation: From Social Login to Enterprise SAML

One of User Pool's most powerful features is external IdP federation. When a user signs in with "Sign in with X" via Google or Facebook, User Pool receives that IdP's token, maps it into its own user directory, and issues your own JWT. In other words, your backend doesn't have to integrate the Google IdP directly — it only has to care about Cognito.

There are two federation modes.

  • OIDC (OpenID Connect): most social logins — Google, Facebook, Apple, Microsoft, etc. Token format is JWT-based.
  • SAML 2.0: the enterprise SSO standard. Okta, AD FS, OneLogin, and Azure AD act as SAML IdPs. Token format is XML-based.
[ B2B enterprise SSO scenario ]

Employee (company staff)
   │ "Sign in with Company SSO" in the Hosted UI
   ▼
User Pool (App Client: web-app)
   │ Generate SAML AuthnRequest
   ▼
Company Okta / Azure AD (SAML IdP)
   │ Employee ID/password + MFA
   │ SAML Assertion (XML)
   ▼
User Pool ── attribute mapping (email, dept, employee_id)
   │ Auto-create/update Cognito User
   │ Issue JWT
   ▼
App / API Gateway

Enterprise SSO scenarios show up frequently on the exam, and the answer is almost always "User Pool + external SAML IdP." A common trap is choosing "Identity Pool alone" for a scenario where "company employees log into an internal system" — but Identity Pool has no directory of its own, so a User Pool must always sit in front of it. Another trap is the "Cognito vs IAM Identity Center" distinction: the standard division of labor is Cognito for external app users (customer-facing apps), IAM Identity Center for employee SSO into the AWS console. "Employees SSO into the AWS console/CLI" is IAM Identity Center; "employees SSO into the SaaS we built" is Cognito User Pool + SAML.

📚 Case study: In the 2021 Atlassian Confluence migration where Okta split off a company, the Cognito User Pool + SAML pattern was used at scale. To leave the existing Confluence's own authentication in place while letting the same employees also come into the new (AWS-based) system via SSO, Cognito acted as the SAML adapter. It's frequently cited as a pattern for gradual migration.

Lambda Triggers: Injecting Code Into the Login Flow

Another powerful User Pool feature is Lambda Triggers. You can invoke a custom Lambda at each stage of the login flow (before sign-up, after sign-up, before authentication, after authentication, before token generation, etc.), letting you handle demands like "only allow company-domain emails to sign up," "sync user info to your own DB on login," or "add a custom claim to the JWT" in code.

TriggerWhen it firesCommon uses
PreSignUpJust before sign-upDomain validation ("only allow @mycompany.com"), auto-confirm external IdP users
PostConfirmationAfter email/SMS verificationReplicate user info to DynamoDB, send a welcome email
PreAuthenticationJust before loginIP blocking, custom validation
PostAuthenticationAfter successful loginRecord login history, refresh user info
PreTokenGenerationJust before JWT issuanceAdd custom claims (e.g., tenant_id), dynamically assign groups
Custom AuthPasswordless auth flowspasswordless, magic link, OTP
Migrate UserGradual move from an existing systemVerify the password against the old DB, then migrate the user to Cognito
# PreSignUp Lambda: only allow company-domain emails
def lambda_handler(event, context):
    email = event["request"]["userAttributes"]["email"]
    if not email.endswith("@mycompany.com"):
        raise Exception("Only @mycompany.com emails allowed")
    # auto-confirm users coming from an external IdP
    if event["triggerSource"].startswith("PreSignUp_ExternalProvider"):
        event["response"]["autoConfirmUser"] = True
        event["response"]["autoVerifyEmail"] = True
    return event

The most operationally important trigger is Migrate User. When you're gradually migrating from an existing homegrown membership system to Cognito, the first time a user tries to log into Cognito, the user doesn't exist there yet. The Migrate User Lambda is invoked, verifies the password against the old DB, and on success auto-creates the user in Cognito. The user becomes a Cognito user naturally, without ever having to reset their password. It's practically the standard pattern for large-scale services where a big-bang migration is too risky.

🔍 Going deeper: The PreTokenGeneration Lambda is central in multi-tenant SaaS. If you stamp the user's tenant_id, role, and feature flags into the JWT as custom claims, the backend can decide permissions from the JWT alone, with no separate DB lookup. Just be aware that if the JWT grows too large it can hit HTTP header limits (commonly 8KB), so the best practice is not to stuff too many claims — include only the essentials. A safer pattern for large permission data is to keep it in a backend cache (e.g., Redis) and put only a reference in the JWT.

Advanced Security and Adaptive Authentication

User Pool's Advanced Security Features (extra cost) provide ML-based threat detection. There are three core capabilities.

  • Compromised Credentials Detection: checks whether the password a user entered appears in a known breached-password database (Have I Been Pwned and the like). On a match, block sign-up or force a change.
  • Adaptive Authentication: uses ML to evaluate the risk of a login attempt (IP reputation, device, region, time) and assigns a risk score (Low/Medium/High). Based on the risk, it automatically requires MFA or blocks.
  • Risk-based Actions: configure automatic actions per risk score (e.g., block on High, require MFA on Medium).
[ Adaptive authentication flow ]

User login attempt
   │ IP, device fingerprint, geolocation
   ▼
Cognito ML model
   │ compute risk score (Low/Medium/High)
   ├─ Low → normal login
   ├─ Medium → require additional MFA
   └─ High → block login + alert

MFA options come in three flavors too — SMS, TOTP (Google Authenticator and the like), and, added in 2023, WebAuthn/Passkeys (biometrics, hardware keys). SMS MFA carries cost and SIM-swap attack risk, so TOTP or WebAuthn is recommended.

⚠️ Pitfall: Many people mistakenly pick GuardDuty for the "evaluate login risk and auto-require MFA" scenario, but GuardDuty is AWS account/resource threat detection (analyzing VPC Flow, CloudTrail) — not user-login threat detection. User-authentication threat detection is Cognito Advanced Security.

Comparison With Other IdP Services

Placing Cognito's design side by side with other identity services makes the trade-offs sharp.

ServiceModelAWS integrationPricingOperational burden
Cognito User PoolUser directory + JWTDeep (API GW/ALB/AppSync integration)MAU-based (50K free)Low
Cognito Identity PoolIssues STS RolesIssues AWS credentials directlyNearly freeLow
IAM Identity CenterEmployee SSO + AWS consoleAWS console/CLI onlyFree (extra cost for Active Directory)Low
Auth0 / Okta CICIdentity SaaSExternal SaaS integrationMAU-based (pricier than Cognito)Very low
Firebase AuthIdentity SaaSGoogle ecosystemFree + some paidVery low
Roll your own (Passport.js)Hand-written codeTotal freedomInfra cost onlyVery high
Keycloak (self-hosted)Open-source OIDC/SAMLFlexibleInfra costHigh

Cognito's strengths are its integration with the AWS ecosystem (especially API Gateway JWT Authorizer, AppSync Auth, and ALB authentication) and its free tier (50K MAU). Its weaknesses are that UI customization is less free than Auth0/Okta, and the Hosted UI is somewhat rough around the edges. So a SaaS where "top-tier user experience" is the crux goes to Auth0/Okta, while "building fast inside the AWS ecosystem" points to Cognito.

Getting Hands-On With the CLI

# Create a User Pool (MFA optional, strong password policy)
aws cognito-idp create-user-pool \
  --pool-name saa-users \
  --mfa-configuration OPTIONAL \
  --auto-verified-attributes email \
  --policies '{"PasswordPolicy":{"MinimumLength":12,"RequireUppercase":true,"RequireLowercase":true,"RequireNumbers":true,"RequireSymbols":true,"TemporaryPasswordValidityDays":3}}' \
  --schema 'Name=email,AttributeDataType=String,Required=true,Mutable=false' \
  --user-pool-add-ons AdvancedSecurityMode=ENFORCED
 
# App Client (no secret = SPA/mobile)
aws cognito-idp create-user-pool-client \
  --user-pool-id ap-northeast-2_XXXXXXXXX \
  --client-name web-app \
  --no-generate-secret \
  --allowed-o-auth-flows code \
  --allowed-o-auth-scopes openid email profile \
  --callback-urls "https://myapp.com/callback" \
  --logout-urls "https://myapp.com/logout" \
  --supported-identity-providers COGNITO Google \
  --token-validity-units '{"AccessToken":"minutes","IdToken":"minutes","RefreshToken":"days"}' \
  --access-token-validity 15 \
  --id-token-validity 15 \
  --refresh-token-validity 30
 
# Add an external IdP (Google)
aws cognito-idp create-identity-provider \
  --user-pool-id ap-northeast-2_XXXXXXXXX \
  --provider-name Google \
  --provider-type Google \
  --provider-details '{"client_id":"...","client_secret":"...","authorize_scopes":"openid email profile"}' \
  --attribute-mapping '{"email":"email","name":"name"}'
 
# Create an Identity Pool (accept User Pool tokens only)
aws cognito-identity create-identity-pool \
  --identity-pool-name saa-id-pool \
  --no-allow-unauthenticated-identities \
  --cognito-identity-providers ProviderName=cognito-idp.ap-northeast-2.amazonaws.com/ap-northeast-2_XXXXXXXXX,ClientId=YYYYYYYYY
 
# Map IAM Roles to the Identity Pool
aws cognito-identity set-identity-pool-roles \
  --identity-pool-id ap-northeast-2:zzzzzzzz \
  --roles authenticated=arn:aws:iam::111:role/CognitoAuthRole
 
# JWKS endpoint for token verification
curl https://cognito-idp.ap-northeast-2.amazonaws.com/ap-northeast-2_XXXXXXXXX/.well-known/jwks.json
 
# Force sign-out (invalidate all of a user's refresh tokens)
aws cognito-idp admin-user-global-sign-out \
  --user-pool-id ap-northeast-2_XXXXXXXXX \
  --username user@example.com

Wrapping Up

The core of Cognito is the split between User Pool (who you are + JWT) ↔ Identity Pool (temporary delegation of an AWS Role). The two pools answer different questions, and scenarios like "a mobile app uploading directly to S3" require combining both. A JWT is hard to invalidate instantly after issuance, so the standard is short lifetimes + a refresh pattern, with GlobalSignOut able to cut off only refresh tokens. For external IdP federation, you just add an OIDC (social) or SAML (enterprise) IdP to the User Pool, and the answer to B2B enterprise SSO scenarios is almost always "User Pool + SAML IdP." Lambda Triggers let you inject custom code into the login flow, and the Migrate User trigger is the standard tool for gradual migration from an existing system. Advanced Security provides ML-based threat detection + adaptive MFA.

In the next article we'll look at security after user authentication — the services that detect and block attacks coming in from outside (WAF, Shield, GuardDuty, Inspector, Macie, Security Hub). If Cognito handles "the path legitimate users come in by," the next topic handles "the path malicious traffic comes in by," and you need both together to make a complete security domain.

📝 Practice Questions

Click a choice to reveal the answer and explanation.

Question 1

A mobile app needs to temporarily grant authenticated users permission to upload images directly to an S3 bucket. What is the most suitable solution?

Question 2

A company wants its employees to log into a SaaS app via the company's Okta SAML SSO. What is the most suitable configuration?

Question 3

An API Gateway HTTP API needs to verify JWTs issued by a Cognito User Pool. What is the most suitable approach?

Question 4

A user's password is suspected to be compromised. You want to immediately terminate all of that user's active sessions. What is the most appropriate action?

Question 5

A company wants to allow only company-domain emails (@mycompany.com) at sign-up. What is the most appropriate approach?

Question 6

A SaaS wants to gradually migrate from its existing homegrown membership system (MySQL + bcrypt) to a Cognito User Pool. How can users move over naturally without resetting their passwords?

Question 7

A company wants to automatically require MFA for login attempts from risky IPs, regions, or devices. What is the most suitable feature?

PreviousSecrets Manager, Parameter Store, and CloudHSM: Three Tools for Handling Secrets and ConfigurationWeek 8 · Day 2Next WAF, Shield, GuardDuty, Inspector, Macie: The 5 Pillars of Cloud Security OperationsWeek 8 · Day 4

Official references

  • SAA-C03 official exam page(opens in a new tab)
  • Amazon Cognito documentation(opens in a new tab)
  • AWS Lambda documentation(opens in a new tab)
  • Amazon S3 documentation(opens in a new tab)
  • AWS IAM documentation(opens in a new tab)
  • IAM Identity Center documentation(opens in a new tab)

On this page

  • User Pool vs Identity Pool: Two Pools Answering Different Questions
  • The Three Tokens and the Traps of Token Verification
  • External IdP Federation: From Social Login to Enterprise SAML
  • Lambda Triggers: Injecting Code Into the Login Flow
  • Advanced Security and Adaptive Authentication
  • Comparison With Other IdP Services
  • Getting Hands-On With the CLI
  • Wrapping Up
  • Practice Questions