JSTGTECH
← Back to blog

Service spotlight: Amazon Cognito user pools vs identity pools

3 min read

Half the confusion I’ve seen around Cognito isn’t about how it works — it’s that “Cognito” is the marketing name for two separate services that happen to share a console tab. Get the two confused and you either build an auth flow that can’t touch AWS resources, or wire up AWS credentials for users who were never actually authenticated.

What it actually is

User pools are the authentication half: a managed user directory that handles sign-up, sign-in, password policies, MFA, and email/SMS verification, and issues JWTs (ID, access, and refresh tokens) on successful login. They can front their own hosted UI or federate to social IdPs and SAML/OIDC providers, so “login with Google” and “login with your corporate Okta” both terminate in the same user pool.

Identity pools (Cognito Federated Identities) are the authorization half: given a token — from a Cognito user pool, a social IdP, SAML, or even an unauthenticated “guest” request — an identity pool exchanges it for temporary AWS credentials via STS, scoped by an IAM role. That’s the piece that lets a mobile app upload directly to S3 or call DynamoDB without a backend in the middle, using credentials that expire instead of a long-lived API key baked into the app.

The two compose but aren’t interchangeable: a user pool alone gives you “who is this person,” not “what can they touch in AWS.” An identity pool alone will happily hand out credentials to unauthenticated guests if you let it — it doesn’t do authentication itself, it just brokers whatever token you hand it.

Where it earns its keep

  • You skip building and hardening your own auth service. Password hashing, MFA enrollment, account recovery, and JWT issuance are the kind of thing that’s deceptively easy to get 90% right and dangerously easy to get the last 10% wrong (timing attacks on password comparison, token replay, session fixation). Cognito’s user pools cover that surface area so it’s not your team’s to maintain.
  • Direct-to-AWS access without a backend proxy. A static site or mobile app can let an authenticated user write straight to an S3 prefix scoped to their identity (${cognito-identity.amazonaws.com:sub} in the IAM policy) instead of routing every upload through an API Gateway + Lambda pair whose only job is forwarding bytes.
  • Federation is handled once, centrally. Add a SAML IdP or a social login provider to the user pool and every app using it gets that login option — you’re not reimplementing OAuth handshakes per client.

Where it goes wrong in practice

The tier structure is the sharpest edge. Cognito’s current pricing splits user pools into Lite, Essentials, and Plus feature plans, and features that look like they should be table stakes — SAML/OIDC federation, advanced security features like compromised-credential and risk-based adaptive authentication — only exist on Essentials or Plus, billed per monthly active user (MAU). A team that prototypes on Lite because the free allowance looks generous, then adds “sign in with our IdP” for an enterprise customer, discovers that single feature moves the entire pool’s MAU count onto the paid tier, not just the new users using it. Check which tier a feature needs before you promise it in a sprint.

The other recurring mistake is treating identity pool credentials as a substitute for real authorization. STS credentials scoped to an identity-pool IAM role are still full IAM credentials — if the attached role is too broad, an authenticated (or worse, guest-enabled) unauthenticated identity can reach far more than “upload your own profile picture.” Audit identity pool roles the same way you’d audit any IAM role handed to untrusted clients, with Condition blocks scoping access to the caller’s own identity ID, not just a wide S3 prefix.

A practical tip

Before adding a Cognito feature to a design doc, check the feature plan comparison for the tier it actually requires — “advanced security” and third-party federation are the two that most often get assumed as free defaults and aren’t. And if you’re issuing AWS credentials via an identity pool, run aws sts get-caller-identity with a test unauthenticated identity’s credentials before shipping, to confirm guest access is scoped as tightly as you think it is rather than as tightly as you meant it to be.

Related posts