/Amazon Cognito
Concept
Medium

Amazon Cognito

10 min read·CognitoAuthenticationOAuthDVA-C02

A comprehensive deep dive into Amazon Cognito — User Pools, Identity Pools, JWT tokens, OAuth 2.0 flows, Lambda triggers, federation, hosted UI, and mobile/web authentication patterns for the DVA-C02 exam.


What is Amazon Cognito?

Amazon Cognito is a fully managed identity service for web and mobile applications. It handles user authentication (sign-up, sign-in, MFA) and AWS authorization (exchanging tokens for temporary AWS credentials) so you don't have to build and maintain an identity system from scratch.

Core mental model: Cognito has two halves. User Pools answer "Who are you?" — they authenticate users and issue JWTs. Identity Pools answer "What AWS resources can you access?" — they exchange tokens for temporary AWS credentials.


Part 1 — Cognito User Pools

A User Pool is a fully managed user directory. It stores usernames, passwords, email addresses, phone numbers, and custom attributes. It handles the full authentication lifecycle including sign-up, sign-in, password reset, email/SMS verification, and MFA.

Rendering diagram…

User Pool Features

FeatureDetail
User registrationSelf-service sign-up with email/phone verification
Sign-in methodsUsername, email, phone number
Social sign-inGoogle, Facebook, Apple, Amazon (via OIDC/SAML federation)
MFATOTP authenticator app or SMS
Password policyMinimum length, complexity requirements
Account recoveryEmail or SMS verification code
Custom attributesUp to 25 custom attributes (immutable once set)
Lambda triggersHook into every step of the auth flow
Hosted UIPre-built, customizable sign-in page
Advanced securityCompromised credential detection, adaptive auth

Creating a User Pool (SDK)

javascript
1import {
2  CognitoIdentityProviderClient,
3  InitiateAuthCommand,
4  SignUpCommand,
5  ConfirmSignUpCommand,
6  ForgotPasswordCommand,
7  ConfirmForgotPasswordCommand,
8} from '@aws-sdk/client-cognito-identity-provider';
9
10const cognito = new CognitoIdentityProviderClient({ region: 'us-east-1' });
11
12const CLIENT_ID = process.env.COGNITO_CLIENT_ID;
13
14// 1. Register a new user
15await cognito.send(new SignUpCommand({
16  ClientId: CLIENT_ID,
17  Username: 'alice@example.com',
18  Password: 'SecurePass123!',
19  UserAttributes: [
20    { Name: 'email', Value: 'alice@example.com' },
21    { Name: 'name',  Value: 'Alice Johnson' },
22    { Name: 'custom:tier', Value: 'premium' },
23  ],
24}));
25
26// 2. Confirm registration (code sent to email)
27await cognito.send(new ConfirmSignUpCommand({
28  ClientId: CLIENT_ID,
29  Username: 'alice@example.com',
30  ConfirmationCode: '123456',
31}));
32
33// 3. Sign in — USER_PASSWORD_AUTH flow
34const { AuthenticationResult } = await cognito.send(new InitiateAuthCommand({
35  ClientId: CLIENT_ID,
36  AuthFlow: 'USER_PASSWORD_AUTH',
37  AuthParameters: {
38    USERNAME: 'alice@example.com',
39    PASSWORD: 'SecurePass123!',
40  },
41}));
42
43const { IdToken, AccessToken, RefreshToken } = AuthenticationResult;
44
45// 4. Refresh tokens
46const refreshed = await cognito.send(new InitiateAuthCommand({
47  ClientId: CLIENT_ID,
48  AuthFlow: 'REFRESH_TOKEN_AUTH',
49  AuthParameters: { REFRESH_TOKEN: RefreshToken },
50}));

JWT Tokens

Cognito issues three tokens on successful authentication:

TokenPurposeContainsTypical use
ID TokenProves user identityUser attributes (email, name, custom:tier, sub)Send to backend to identify the user
Access TokenAuthorizes API callsOAuth 2.0 scopes, username, groupsSend as Authorization: Bearer header
Refresh TokenGets new ID + Access tokensOpaqueStore securely; exchange when Access Token expires

Token Expiry Defaults

TokenDefault expiryRange
ID Token1 hour5 min – 24 hours
Access Token1 hour5 min – 24 hours
Refresh Token30 days1 hour – 10 years

Validating a JWT in Your Backend

javascript
1import { CognitoJwtVerifier } from 'aws-jwt-verify';
2
3const verifier = CognitoJwtVerifier.create({
4  userPoolId: 'us-east-1_AbCdEfGhI',
5  tokenUse: 'access',           // 'id' or 'access'
6  clientId: process.env.COGNITO_CLIENT_ID,
7});
8
9// Express middleware
10async function requireAuth(req, res, next) {
11  try {
12    const token = req.headers.authorization?.replace('Bearer ', '');
13    const payload = await verifier.verify(token);
14
15    req.user = {
16      sub:   payload.sub,          // unique user ID — use as your userId
17      email: payload.email,
18      tier:  payload['custom:tier'],
19      groups: payload['cognito:groups'] ?? [],
20    };
21
22    next();
23  } catch {
24    res.status(401).json({ error: 'Unauthorized' });
25  }
26}

Authentication Flows

Cognito supports multiple OAuth 2.0 / custom authentication flows:

FlowUse caseHow it works
USER_PASSWORD_AUTHServer-side appsUsername + password sent directly
USER_SRP_AUTHClient-side appsSecure Remote Password — password never sent in plain text
REFRESH_TOKEN_AUTHToken renewalExchange refresh token for new ID + Access tokens
CUSTOM_AUTHPasswordless, OTPLambda triggers define custom challenge/response
ADMIN_USER_PASSWORD_AUTHServer-side adminRequires server-side secret; bypasses SRP

Hosted UI & OAuth 2.0 Endpoints

Cognito provides a pre-built, customizable sign-in page (Hosted UI) with OAuth 2.0 endpoints — no frontend auth code needed.

text
1# Authorization endpoint — redirect user here to sign in
2GET https://<domain>.auth.us-east-1.amazoncognito.com/oauth2/authorize
3  ?response_type=code
4  &client_id=<CLIENT_ID>
5  &redirect_uri=https://app.example.com/callback
6  &scope=openid+email+profile
7  &state=random-csrf-token
8
9# Token endpoint — exchange authorization code for tokens
10POST https://<domain>.auth.us-east-1.amazoncognito.com/oauth2/token
11  grant_type=authorization_code
12  &code=<AUTH_CODE>
13  &redirect_uri=https://app.example.com/callback
14  &client_id=<CLIENT_ID>
15
16# Logout endpoint
17GET https://<domain>.auth.us-east-1.amazoncognito.com/logout
18  ?client_id=<CLIENT_ID>
19  &logout_uri=https://app.example.com

Supported OAuth 2.0 grant types:

  • Authorization Code — recommended for web/mobile apps (with PKCE for SPAs)
  • Implicit — legacy, deprecated
  • Client Credentials — machine-to-machine (no user involved)

Social & Enterprise Federation

Connect external identity providers so users can sign in with their existing accounts:

Rendering diagram…

Users signing in through any provider get a Cognito JWT — your backend always receives the same token format regardless of the sign-in method.


Lambda Triggers

Lambda triggers let you hook into every stage of the User Pool lifecycle to customize behavior:

TriggerWhen it firesUse case
Pre sign-upBefore user is createdBlock certain email domains, auto-confirm
Post confirmationAfter user confirms accountCreate user record in DynamoDB
Pre authenticationBefore password is checkedBlock sign-in by IP, custom checks
Post authenticationAfter successful sign-inLog activity, update last-login
Pre token generationBefore JWT is issuedAdd custom claims to tokens
Custom messageBefore verification email/SMS is sentCustomize email content
User migrationWhen user signs in but not in poolMigrate users from legacy system
Define auth challengeStart custom auth flowDefine challenge type (OTP, CAPTCHA)
Create auth challengeGenerate the challengeSend OTP via SES/SNS
Verify auth challengeValidate the responseCheck OTP code
javascript
1// Pre Token Generation trigger — add custom claims to the JWT
2exports.handler = async (event) => {
3  // Fetch additional user data from DynamoDB
4  const userTier = await getUserTierFromDB(event.userName);
5
6  event.response = {
7    claimsOverrideDetails: {
8      claimsToAddOrOverride: {
9        'custom:tier':        userTier,
10        'custom:featureFlags': 'dark-mode,beta-dashboard',
11      },
12      claimsToSuppress: ['address'], // remove sensitive claims
13    },
14  };
15
16  return event;
17};
javascript
1// Custom auth flow — passwordless OTP via email
2// 1. Define Auth Challenge
3exports.defineAuthChallenge = async (event) => {
4  event.response.challengeName = 'CUSTOM_CHALLENGE';
5  event.response.issueTokens = false;
6  event.response.failAuthentication = false;
7  return event;
8};
9
10// 2. Create Auth Challenge — send OTP
11exports.createAuthChallenge = async (event) => {
12  const otp = Math.floor(100000 + Math.random() * 900000).toString();
13  await sendOtpEmail(event.request.userAttributes.email, otp);
14
15  event.response.publicChallengeParameters = { destination: event.request.userAttributes.email };
16  event.response.privateChallengeParameters = { answer: otp };
17  return event;
18};
19
20// 3. Verify Auth Challenge — check OTP
21exports.verifyAuthChallenge = async (event) => {
22  event.response.answerCorrect =
23    event.request.challengeAnswer === event.request.privateChallengeParameters.answer;
24  return event;
25};

API Gateway & ALB Integration

User Pools integrate natively with API Gateway and ALB — no Lambda authorizer needed for JWT validation.

javascript
1// Client sends JWT in Authorization header
2const response = await fetch('https://api.example.com/orders', {
3  headers: {
4    Authorization: idToken,   // ID Token for API Gateway Cognito Authorizer
5    // OR
6    Authorization: `Bearer ${accessToken}`, // Access Token for ALB or Lambda authorizer
7  },
8});

API Gateway Cognito Authorizer:

  • Validates the JWT signature using User Pool's JWKS endpoint
  • Checks token expiry and audience (aud claim)
  • Passes requestContext.authorizer.claims to Lambda (email, sub, custom attributes)
  • Cache TTL configurable (0–3600s)

Part 2 — Cognito Identity Pools

An Identity Pool (Federated Identities) exchanges an identity token for temporary AWS credentials via STS. This enables mobile and web apps to access AWS services (S3, DynamoDB, Lambda) directly — without routing through your backend.

Rendering diagram…

Authenticated vs Unauthenticated Roles

Identity Pools map identities to IAM roles:

RoleWhen usedExample use case
Authenticated roleUser has signed inFull read/write to their own S3 prefix
Unauthenticated roleGuest / not signed inRead-only access to public content
json
1// Authenticated IAM role policy — user can only access their own S3 prefix
2{
3  "Effect": "Allow",
4  "Action": ["s3:GetObject", "s3:PutObject"],
5  "Resource": "arn:aws:s3:::user-uploads/${cognito-identity.amazonaws.com:sub}/*"
6}

The ${cognito-identity.amazonaws.com:sub} variable resolves to the user's unique Cognito identity ID — providing automatic per-user data isolation without any backend code.

Getting Credentials from Identity Pool

javascript
1import { CognitoIdentityClient, GetIdCommand, GetCredentialsForIdentityCommand } from '@aws-sdk/client-cognito-identity';
2import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
3
4const identityClient = new CognitoIdentityClient({ region: 'us-east-1' });
5
6// 1. Get Identity ID (maps the user to a Cognito Identity)
7const { IdentityId } = await identityClient.send(new GetIdCommand({
8  AccountId: '123456789012',
9  IdentityPoolId: 'us-east-1:abc-123-def',
10  Logins: {
11    'cognito-idp.us-east-1.amazonaws.com/us-east-1_XYZ': idToken, // from User Pool
12  },
13}));
14
15// 2. Exchange for AWS credentials
16const { Credentials } = await identityClient.send(new GetCredentialsForIdentityCommand({
17  IdentityId,
18  Logins: {
19    'cognito-idp.us-east-1.amazonaws.com/us-east-1_XYZ': idToken,
20  },
21}));
22
23// 3. Use temporary credentials directly
24const s3 = new S3Client({
25  region: 'us-east-1',
26  credentials: {
27    accessKeyId:     Credentials.AccessKeyId,
28    secretAccessKey: Credentials.SecretAccessKey,
29    sessionToken:    Credentials.SessionToken,
30  },
31});
32
33await s3.send(new PutObjectCommand({
34  Bucket: 'user-uploads',
35  Key: `${IdentityId}/profile.jpg`,
36  Body: imageBuffer,
37}));

User Pool vs Identity Pool Summary

FeatureUser PoolIdentity Pool
PurposeAuthentication — who are you?AWS Authorization — what can you access?
OutputJWTs (ID, Access, Refresh tokens)AWS Temporary Credentials (via STS)
Stores users✅ Yes — user directory❌ No — just maps identities to roles
Social sign-in✅ Built-in federation✅ Accepts any OIDC/SAML token
Guest access✅ Unauthenticated role
API Gateway integration✅ Native Cognito Authorizer❌ (use IAM auth with credentials)
Direct AWS access❌ (need backend)✅ App accesses S3/DynamoDB directly

Combined Flow (Most Common Pattern)

Rendering diagram…
  1. User signs in to User Pool → receives JWTs
  2. App exchanges ID Token with Identity Pool → receives temp AWS credentials
  3. App uses credentials to access S3/DynamoDB directly (mobile pattern)
  4. App uses Access Token to call your API Gateway endpoints

DVA-C02 Quick Reference

TopicKey Fact
User Pool outputJWTs (ID, Access, Refresh tokens)
Identity Pool outputTemporary AWS credentials (via STS)
ID Token containsUser attributes (email, name, custom claims)
Access Token containsOAuth scopes and groups
Default Access Token expiry1 hour
Default Refresh Token expiry30 days
Add custom claims to JWTPre Token Generation Lambda trigger
Migrate users on first sign-inUser Migration Lambda trigger
Passwordless OTP authCustom Auth Flow (Define/Create/Verify challenge triggers)
API Gateway integrationCognito User Pool Authorizer (validates JWT)
Guest / unauthenticated accessIdentity Pool unauthenticated role
Per-user S3 isolationIAM policy variable ${cognito-identity.amazonaws.com:sub}
Social sign-inConfigured on User Pool (OIDC / SAML federation)
SAML / Active DirectoryFederated IdP on User Pool OR Identity Pool
Hosted UIPre-built sign-in page on User Pool with OAuth 2.0 endpoints

Practice Questions5

easy

Q1. A developer builds a mobile app with user sign-up, sign-in, and MFA. The app needs to authenticate with an AWS API Gateway. Which Amazon Cognito component handles the user directory and authentication?


Select one answer before revealing.

medium

Q2. After signing in via Cognito User Pool, a user wants to call DynamoDB directly from a mobile app without a backend API. Which sequence of steps is correct?


Select one answer before revealing.

medium

Q3. A developer needs to add custom validation logic during Cognito User Pool sign-up — specifically to reject sign-ups from email domains outside the company. Which Cognito feature enables this?


Select one answer before revealing.

medium

Q4. A Cognito User Pool is configured with hosted UI. A user signs in and then calls the token endpoint to exchange the authorization code for tokens. The developer needs to inspect the token to get the user's email and group memberships. Which token contains this information?


Select one answer before revealing.

easy

Q5. A developer is testing a Cognito User Pool integration. The refresh token keeps expiring after 30 days. The developer wants to extend the refresh token expiry. Where is this configured?


Select one answer before revealing.