Amazon Cognito

Amazon Cognito is AWS's managed customer identity service, split into two purpose-built halves that the exam constantly tests as a pair: User Pools answer "who is this user?" (authentication), and Identity Pools answer "what can this user do in AWS?" (authorization via temporary credentials). This module builds the mental model for how tokens flow between them, then layers on the exam-reasoning patterns DVA-C02 tests around JWTs, federation, and API Gateway integration.

Authentication — User Pools Authorization — Identity Pools Federation — Social / SAML / OIDC
2
Core components (User / Identity Pools)
3
JWT/token types issued by a User Pool
1 hr
Default ID/Access token lifetime
30 days
Default refresh token lifetime

The Two Halves — Memorize This

USER POOLSSign-up / sign-in — issues JWTs — "who are you"
IDENTITY POOLSToken → temporary AWS credentials — "what can you touch"

How Cognito Actually Works

The Core Mechanism — Two Services, One Flow
⚠️ The Recurring Exam Theme

Almost every Cognito question tests one of three things: (1) can you tell whether a scenario needs authentication (User Pool) or AWS authorization/temporary credentials (Identity Pool) — often both together, (2) do you know the difference between the ID token, access token, and refresh token well enough to spot the one being used wrong, or (3) can you identify the right integration point — API Gateway User Pool authorizer for validating a JWT, or Identity Pool + STS for direct AWS service calls.

DVA-C02 Domain Mapping

Exam DomainWhere Cognito Shows Up
Domain 2 — SecurityThe centerpiece — implementing authentication/authorization for applications, choosing User Pools vs Identity Pools, JWT validation, IAM role mapping for federated identities
Domain 1 — Development with AWS ServicesIntegrating the Cognito SDK/Amplify into application code, calling User Pool APIs (sign-up, sign-in, token refresh), handling Hosted UI redirects
Domain 3 — DeploymentConfiguring app clients, callback/redirect URLs, and OAuth scopes correctly per environment (dev/test/prod) as part of a deployment pipeline
Domain 4 — Troubleshooting and OptimizationDiagnosing 401s from an expired or wrong-type token, misconfigured API Gateway authorizers, or IAM role-mapping failures on an Identity Pool

Cognito is one of the most heavily-tested services on DVA-C02 because it sits squarely in the "developer implements security" theme the exam is built around — prioritize it accordingly.

Decision Tree — Mental Model

Step 1 — Authenticate

User submits credentials (or federates via a social/SAML/OIDC IdP) through the User Pool's Hosted UI or SDK. The User Pool validates them, runs any configured Lambda triggers, and issues three JWTs: ID token, Access token, Refresh token.

Step 2 — App Holds Tokens

The client app stores the tokens (securely — never in plain localStorage for production-grade apps) and uses the ID token to know who the user is, or the access token to call authorized backend/resource-server APIs.

Step 3 — Exchange for AWS Credentials

If the app needs to call AWS services directly, it presents the User Pool token to an Identity Pool (GetIdGetCredentialsForIdentity), which internally calls sts:AssumeRoleWithWebIdentity against the mapped IAM role.

Step 4 — Call AWS Directly

The app uses the short-lived temporary AWS credentials to call AWS services directly (e.g. s3:PutObject) — no backend proxy required, keeping operational overhead low.

Step 5 — Validate at the API Layer

For calls to your own backend APIs (not raw AWS APIs), API Gateway's Cognito User Pool authorizer validates the JWT's signature, issuer, audience, and expiration before allowing the request through — no custom Lambda authorizer code needed.

Step 6 — Silent Renewal

When the ID/access token expires (default 1 hour), the app uses the still-valid refresh token to silently obtain a new pair without forcing the user to log in again.

Final Summary

Must Know
  • User Pools = authentication; Identity Pools = authorization/temporary AWS credentials
  • 3 token types: ID (identity claims), Access (scopes/authorization), Refresh (renewal)
  • Identity Pools issue temporary STS credentials — never long-lived IAM keys
  • API Gateway User Pool authorizer validates JWTs natively — no Lambda code required
  • Federated IdPs (social + SAML/OIDC) plug into User Pools or directly into Identity Pools
Must Understand
  • OAuth 2.0 authorization code grant (+ PKCE for public clients) vs the legacy implicit grant
  • What each Lambda trigger is for (pre sign-up, post confirmation, pre token generation, migrate user, custom auth challenge)
  • Authenticated vs unauthenticated (guest) role mapping in an Identity Pool
  • Hosted UI as a single OAuth/OIDC front door across every configured IdP
Can De-prioritize
  • Exact console click-paths for creating a pool
  • Historical SMS-MFA pricing figures
  • SAML metadata XML syntax minutiae

Exam appearance probability: HIGH

Components — Deep Dive

The building blocks the exam actually asks about: what each piece stores, what it issues, and where developers most often trip over the details.

1.1 User Pools — The Authentication Directory Foundational
PurposeManaged user directory: sign-up, sign-in, account confirmation, password reset, MFA
AttributesStandard (email, phone_number, name...) + custom attributes prefixed custom:
1.2 App Clients & Hosted UI High exam relevance
App clientRepresents an application registered against the pool; can have multiple per pool
Client typesPublic (no secret — mobile/SPA, use Authorization Code + PKCE) vs Confidential (has a secret — server-side apps)
1.3 JWT Token Types — ID vs Access vs Refresh Frequent trap
ID TokenOIDC identity claims (sub, email, name...) — proves who the user is to your app; token_use=id
Access TokenOAuth 2.0 scopes/claims for authorization — used to call the User Pool's own API or a protected resource server; token_use=access
Refresh TokenLong-lived, used to silently obtain new ID/Access tokens without re-authenticating; not a JWT
1.4 Lambda Triggers — Customizing the Auth Lifecycle High exam relevance
1.5 Identity Pools (Federated Identities) — The Authorization Layer High exam relevance
PurposeExchange a trusted identity for temporary AWS credentials
Accepted identitiesUser Pool tokens, social IdP tokens, SAML/OIDC assertions, developer-authenticated identities, or unauthenticated (guest) requests
1.6 Federated Identity Providers High exam relevance
Social IdPsGoogle, Facebook, Login with Amazon, Sign in with Apple
Enterprise IdPsSAML 2.0 and OIDC — e.g. Okta, Azure AD / Microsoft Entra ID, ADFS
1.7 User Pool Groups & Custom Attributes Medium

AWS Exam Thinking

Requirement → Keywords → Expected Answer → why every distractor fails. Written in AWS scenario style ("A company needs to...", "What is the MOST appropriate solution?").

Users need self-service sign-up/sign-in with MFA for a customer-facing app
user directorysign-up/sign-inMFA
Expected Answer

Amazon Cognito User Pool

DistractorWhy it's wrong
IAM Identity CenterBuilt for workforce/employee access to AWS accounts and business apps, not millions of customer-facing app end users
IAM usersNot designed for self-service sign-up at scale, no built-in hosted sign-in UI, and each IAM user is a security-sensitive AWS principal
Cognito Identity PoolDoesn't authenticate anyone — it only exchanges an already-trusted identity for AWS credentials
Mobile app must upload files directly to S3 with the LEAST operational overhead
temporary AWS credentialsdirect S3 accessno backend
Expected Answer

Cognito Identity Pool federated with a User Pool → temporary credentials via STS

DistractorWhy it's wrong
Embed IAM user access keys in the appLong-lived credentials shipped inside a mobile app are a severe security anti-pattern — never the correct answer
API Gateway + Lambda proxy for every uploadAdds a backend hop and operational overhead (scaling, monitoring, cost) that direct-to-S3 credentials avoid entirely
Backend-generated pre-signed URLs per uploadWorks, but still requires a backend call for every single upload — Identity Pool credentials remove that dependency, giving the lowest operational overhead
Validate a JWT at the API layer with NO custom authorization code to write or maintain
JWT validationno custom Lambdamanaged authorizer
Expected Answer

API Gateway Cognito User Pool authorizer (REST API) / JWT authorizer (HTTP API)

DistractorWhy it's wrong
Lambda authorizerWorks, but requires you to write and maintain custom validation code — more operational overhead than the native, managed authorizer
IAM authorization (SigV4)Designed for AWS-principal callers signing requests with AWS credentials, not for end users who authenticated via Cognito
API keysIdentify and throttle a calling application/plan — they do not authenticate an individual user
Enterprise employees must sign in with corporate Active Directory credentials via SAML
SAML 2.0enterprise IdPcorporate directory
Expected Answer

Cognito User Pool with a SAML identity provider configured, exposed via Hosted UI

DistractorWhy it's wrong
Social IdP (Google/Facebook)Wrong protocol entirely for corporate AD/ADFS federation
Identity Pool alone, no User PoolPossible in theory, but doesn't provide the managed interactive sign-in/redirect experience the Hosted UI gives you with a User Pool
IAM Identity CenterValid for federating workforce access into the AWS Console/CLI, but not the mechanism for authenticating end users into a custom application via Cognito
Inject a custom, business-logic-derived claim into every issued token
custom claimsmodify token contents
Expected Answer

Pre Token Generation Lambda trigger

DistractorWhy it's wrong
Post Authentication triggerFires after authentication succeeds, but cannot modify the contents of the tokens about to be issued
Custom attributes aloneCustom attributes are stored user data — they aren't automatically injected as token claims without a Pre Token Generation trigger doing the mapping
Pre Sign-up triggerRuns before the account even exists, far too early to influence a specific sign-in's token contents
Migrate users from a legacy on-prem directory as they log in, with no bulk import job
lazy migrationlegacy directoryno batch job
Expected Answer

Migrate User (User Migration) Lambda trigger

DistractorWhy it's wrong
Bulk import via CSVRequires an upfront batch job for every user — explicitly what the requirement says to avoid
Federation via SAML/OIDCWould require the legacy directory to speak SAML/OIDC; overkill for a straightforward username/password validation migration
Post Confirmation triggerFires after confirmation of a Cognito-native account — not the trigger involved in validating credentials against the legacy store during migration
Give unauthenticated users limited, read-only access to specific AWS resources
guest accessunauthenticated
Expected Answer

Cognito Identity Pool unauthenticated (guest) role

DistractorWhy it's wrong
User Pool with relaxed confirmation rulesStill requires a sign-up flow — not truly "unauthenticated"
Public S3 bucket policy / anonymous accessGrants blanket access with no per-session credential scoping, expiry, or ability to later differentiate guest vs authenticated permissions
Lambda authorizer that allows everythingDoesn't produce scoped, temporary AWS credentials usable for direct SDK calls the way an Identity Pool guest role does
A protected internal API expects OAuth scopes, but the app is sending the ID token
wrong token type401 errorsscopes
Expected Answer

Send the Access token instead — it carries token_use=access and OAuth scopes; the ID token does not

DistractorWhy it's wrong
Keep using the ID tokenThe ID token carries identity claims (who the user is), not authorization scopes — resource servers expecting scopes will reject or mishandle it
Send the refresh token to the APIRefresh tokens are never presented to resource servers — they exist solely to obtain new ID/Access tokens
Request a fresh token from the Identity PoolIdentity Pools don't issue application-level authorization tokens — they issue AWS credentials, an entirely different credential type

Integrations & Architecture

2 — Where Cognito Plugs In

Amazon API Gateway
WhatCognito User Pool authorizer (REST APIs) / JWT authorizer (HTTP APIs)
HowValidates the token's signature against the User Pool's JWKS, checks issuer/audience/token_use/expiration — no Lambda code needed
PatternClient sends ID or Access token (per authorizer config) in the Authorization header → API Gateway validates before invoking the backend integration
AWS IAM
WhatIdentity Pool authenticated/unauthenticated default roles, plus rule-based role mapping
WhyLets federated app users assume scoped IAM permissions without ever holding a long-lived AWS credential
PatternRole trust policy trusts cognito-identity.amazonaws.com, conditioned on cognito-identity.amazonaws.com:aud = the Identity Pool ID (and :amr for authenticated vs unauthenticated)
AWS STS
WhatIssues the actual temporary AWS credentials behind every Identity Pool exchange
HowGetCredentialsForIdentity internally calls sts:AssumeRoleWithWebIdentity against the mapped IAM role
WhyCredentials are short-lived and scoped to exactly the mapped role's permissions — no static keys ever touch the client
AWS Lambda
WhatEvery User Pool lifecycle trigger (pre sign-up, post confirmation, pre token generation, migrate user, custom auth challenge, etc.)
WhyThe only way to inject custom business logic into an otherwise fully managed authentication flow
Amazon S3
WhatA common direct target for Identity Pool temporary credentials
WhyLets a mobile/web client upload or download objects directly, scoped by an IAM policy (often restricted to a per-user prefix using policy variables like ${cognito-identity.amazonaws.com:sub}) — no backend proxy required

3 — End-to-End Architecture Examples

Example 1 — Mobile App Uploading Directly to S3

A photo-sharing mobile app needs authenticated users to upload images straight to S3 without routing every upload through a backend server.

Example 2 — Serverless API with Cognito-Authenticated Access

A web application needs a secured backend API, without managing servers or writing custom auth-validation code.

⚠️ The Identity Pool Necessity Test

Ask: does the client need to call an AWS service API directly (S3, DynamoDB, Kinesis, etc.)? If yes → you need an Identity Pool to mint temporary AWS credentials. If the client only ever talks to your own backend/API Gateway, which then talks to AWS services using its own execution role, an Identity Pool is unnecessary — a User Pool alone (for authentication) plus an API Gateway authorizer (for authorization at the API layer) is sufficient.

Best Practices & Common Exam Traps

4 — When to Use / When Not To

Use Cognito When...
  • You need a managed, scalable directory for customer-facing application users (not AWS workforce access)
  • You need to support social and/or enterprise (SAML/OIDC) sign-in without building the federation logic yourself
  • Clients need to call AWS services (S3, DynamoDB, etc.) directly with least operational overhead
  • You need standards-based JWTs your API Gateway or backend can validate with no custom code
Don't Use Cognito When...
  • You're authenticating AWS workforce users into the AWS Console/CLI — use IAM Identity Center instead
  • You're authorizing service-to-service (machine-to-machine, no end user) calls within your own AWS account — use IAM roles directly, not Cognito
  • You only need simple API-key based throttling/identification of calling apps with no real user identity — plain API Gateway API keys/usage plans may be enough

5 — User Pools vs Identity Pools

AspectUser PoolsIdentity Pools
Answers"Who is this user?""What AWS resources can this user touch?"
FunctionAuthentication — user directory, sign-up/sign-inAuthorization — token-to-AWS-credential exchange
OutputJWTs: ID token, Access token, Refresh tokenTemporary AWS credentials (access key, secret key, session token) via STS
Typical consumerYour own backend / API Gateway authorizerAWS SDK calls made directly from the client (S3, DynamoDB, etc.)
Supports guests?No — every principal is a registered/confirmed userYes — built-in unauthenticated (guest) role
Needs the other?Optional — can stand alone for pure app authenticationOptional — can federate directly with a social/SAML/OIDC IdP, but is most often layered on top of a User Pool

6 — Common Exam Traps

MisconceptionReality
"The ID token and Access token are interchangeable"ID token = identity claims (who); Access token = authorization scopes (what they can do). Sending the wrong one to a resource server is a classic bug and exam trap
"An Identity Pool authenticates the user"Identity Pools never authenticate anyone — they only exchange an already-trusted identity for temporary AWS credentials
"Cognito Identity Pools hand out permanent IAM access keys"They always issue short-lived, temporary STS credentials tied to a mapped IAM role — never static long-lived keys
"You must always use both a User Pool and an Identity Pool together"A User Pool can stand alone for pure application authentication; an Identity Pool is only needed when the client must call AWS service APIs directly
"API Gateway needs a Lambda authorizer to work with Cognito"API Gateway has a native Cognito User Pool authorizer (REST) / JWT authorizer (HTTP) that validates tokens with zero custom code
"Custom attributes automatically show up as token claims"They don't, by default — a Pre Token Generation Lambda trigger is required to inject custom claims into issued tokens
"Refresh tokens are sent to protected APIs like access tokens"Refresh tokens are only ever exchanged with the User Pool token endpoint to mint new ID/Access tokens — never presented to a resource server

Memory Anchors

User Pools = Authentication (AuthN)

"I need users to sign up and log in" → User Pool. Full stop.

Identity Pools = Authorization to AWS (AuthZ)

"I need logged-in users to call AWS services directly" → Identity Pool, usually layered on a User Pool as the trusted IdP.

ID = Identity, Access = Authorization, Refresh = Renewal

Three tokens, three distinct jobs — never assume they're interchangeable.

API Gateway authorizer validates; Identity Pool issues credentials

Two entirely different problems that both start with "how do I secure this call" — don't reach for an Identity Pool just to protect a backend API endpoint.

Hands-On Lab

Three self-contained exercises that turn the token flow from the Overview tab into real AWS CLI v2 calls. Everything runs in eu-west-1. Work through them in order — Exercise 1 produces the tokens that Exercises 2 and 3 consume.

Lab 1 — User Pool, Test User, and Decoding the JWTs

Create a User Pool and app client, provision a ready-to-use test user without an email/SMS confirmation round-trip, authenticate via the CLI, and inspect the raw claims inside the ID and Access tokens to see the difference the guide describes conceptually.

  1. Create the User Pool.
    aws cognito-idp create-user-pool \
      --pool-name dva-lab-pool \
      --region eu-west-1 \
      --auto-verified-attributes email \
      --policies '{"PasswordPolicy":{"MinimumLength":8,"RequireUppercase":true,"RequireLowercase":true,"RequireNumbers":true,"RequireSymbols":false}}'
    JSON output containing "UserPool": { "Id": "eu-west-1_AbCdEfGhI", ... }. Copy this pool ID — every later command needs it.
  2. Create a public app client (no secret, so initiate-auth doesn't need a SECRET_HASH).
    aws cognito-idp create-user-pool-client \
      --user-pool-id eu-west-1_AbCdEfGhI \
      --client-name dva-lab-client \
      --no-generate-secret \
      --explicit-auth-flows ALLOW_USER_PASSWORD_AUTH ALLOW_REFRESH_TOKEN_AUTH \
      --region eu-west-1
    "UserPoolClient": { "ClientId": "1a2b3c4d5e6f7g8h9i0j1k2l3m", ... }.
  3. Create the test user and set a permanent password in one shot. Using admin-create-user + admin-set-user-password --permanent skips the verification-code step entirely — the fastest way to get a confirmed user for a lab.
    aws cognito-idp admin-create-user \
      --user-pool-id eu-west-1_AbCdEfGhI \
      --username dva-lab-user \
      --user-attributes Name=email,Value=dva-lab-user@example.com Name=email_verified,Value=true \
      --message-action SUPPRESS \
      --region eu-west-1
    
    aws cognito-idp admin-set-user-password \
      --user-pool-id eu-west-1_AbCdEfGhI \
      --username dva-lab-user \
      --password 'DvaLab2026!' \
      --permanent \
      --region eu-west-1
    User status becomes CONFIRMED (check with admin-get-user) — no verification email/SMS round-trip needed.
  4. Authenticate and capture all three tokens.
    aws cognito-idp initiate-auth \
      --auth-flow USER_PASSWORD_AUTH \
      --client-id 1a2b3c4d5e6f7g8h9i0j1k2l3m \
      --auth-parameters USERNAME=dva-lab-user,PASSWORD='DvaLab2026!' \
      --region eu-west-1
    A JSON AuthenticationResult block with IdToken, AccessToken, and RefreshToken — three long dot-separated strings. Save the ID and Access tokens as ID_TOKEN and ACCESS_TOKEN — later labs reuse them.
  5. Decode the payload (2nd segment) of the ID token and the Access token and compare claims. Base64url segments are often missing padding, so a plain base64 -d one-liner can fail — pad it first, or use Python, which handles this cleanly.
    ID_TOKEN="<paste IdToken here>"
    ACCESS_TOKEN="<paste AccessToken here>"
    
    decode_jwt() {
      python3 -c "
    import sys, json, base64
    p = sys.argv[1].split('.')[1]
    p += '=' * (-len(p) % 4)
    print(json.dumps(json.loads(base64.urlsafe_b64decode(p)), indent=2))
    " "$1"
    }
    
    decode_jwt "$ID_TOKEN"
    decode_jwt "$ACCESS_TOKEN"
    The ID token's JSON shows "token_use": "id" plus identity claims (email, email_verified, cognito:username). The Access token's JSON shows "token_use": "access", a "client_id" claim, and a "scope" claim — and notably has no email claim, because it isn't meant to carry identity data.
⚠️ Why this matters for the exam

DVA-C02 loves the scenario "a resource server rejects a Cognito token — why?" Seeing with your own eyes that the ID token has no scope claim and the Access token has no email claim makes the "wrong token sent to the wrong consumer" trap (covered in Components 1.3 and Exam Logic) permanently obvious instead of a memorized rule.

Lab 2 — JWT Authorizer on an HTTP API Route

Attach the User Pool as a native JWT authorizer on an API Gateway HTTP API route, then call the route with and without a valid Authorization header to see the 401 vs 200 behavior the Integrations tab describes.

  1. Create the HTTP API.
    aws apigatewayv2 create-api \
      --name dva-lab-http-api \
      --protocol-type HTTP \
      --region eu-west-1
    "ApiId": "abc123xyz4" and an "ApiEndpoint": "https://abc123xyz4.execute-api.eu-west-1.amazonaws.com". Save both.
  2. Add a backend integration. To keep the lab self-contained (no Lambda to deploy), proxy to a public echo endpoint — the point of this exercise is the authorizer's behavior, not the backend logic.
    aws apigatewayv2 create-integration \
      --api-id abc123xyz4 \
      --integration-type HTTP_PROXY \
      --integration-method GET \
      --integration-uri https://httpbin.org/anything \
      --payload-format-version 1.0 \
      --region eu-west-1
    "IntegrationId": "i1j2k3l4".
  3. Create the JWT authorizer, pointing it at the User Pool's issuer URL. The audience is the app client ID — API Gateway matches it against either the aud claim (ID tokens) or the client_id claim (Access tokens).
    aws apigatewayv2 create-authorizer \
      --api-id abc123xyz4 \
      --authorizer-type JWT \
      --identity-source '$request.header.Authorization' \
      --name dva-lab-cognito-jwt-authorizer \
      --jwt-configuration Audience=1a2b3c4d5e6f7g8h9i0j1k2l3m,Issuer=https://cognito-idp.eu-west-1.amazonaws.com/eu-west-1_AbCdEfGhI \
      --region eu-west-1
    "AuthorizerId": "auth9z8y7x".
  4. Create a protected route wired to both the integration and the authorizer, then deploy an auto-deploying $default stage.
    aws apigatewayv2 create-route \
      --api-id abc123xyz4 \
      --route-key "GET /secure" \
      --target integrations/i1j2k3l4 \
      --authorization-type JWT \
      --authorizer-id auth9z8y7x \
      --region eu-west-1
    
    aws apigatewayv2 create-stage \
      --api-id abc123xyz4 \
      --stage-name '$default' \
      --auto-deploy \
      --region eu-west-1
    Route and stage created; the API endpoint from Step 1 is live at /secure within a few seconds (auto-deploy).
  5. Call the route with no header.
    curl -i https://abc123xyz4.execute-api.eu-west-1.amazonaws.com/secure
    HTTP/2 401 with a body like {"message":"Unauthorized"} — the JWT authorizer rejects the request before the integration ever runs.
  6. Call it again, sending the Access token from Lab 1 (not the ID token — this is the token type resource servers expect, per Components 1.3).
    curl -i https://abc123xyz4.execute-api.eu-west-1.amazonaws.com/secure \
      -H "Authorization: Bearer ${ACCESS_TOKEN}"
    HTTP/2 200 with the httpbin echo response body — the authorizer validated the token's signature, issuer, and client_id-as-audience, then let the request through with zero custom authorization code.
⚠️ Why this matters for the exam

This is the "validate a JWT with no custom Lambda code" scenario from Exam Logic, made concrete. Also notice you authenticated with USER_PASSWORD_AUTH (a User Pool concern) and authorized the API call with a JWT authorizer (also a User Pool concern) — no Identity Pool was involved anywhere in this lab, because the client never called an AWS service API directly. That's the exact discriminator the Integrations tab's "Identity Pool Necessity Test" trains you to spot.

Lab 3 — Identity Pool: Trading a Token for Temporary AWS Credentials

Create an Identity Pool that trusts the User Pool as an authentication provider, walk through GetIdGetCredentialsForIdentity by hand, and use the resulting temporary credentials to prove they are real, scoped, short-lived AWS credentials — not another JWT.

  1. Create the Identity Pool linked to the User Pool app client.
    aws cognito-identity create-identity-pool \
      --identity-pool-name dva_lab_identity_pool \
      --no-allow-unauthenticated-identities \
      --cognito-identity-providers ProviderName=cognito-idp.eu-west-1.amazonaws.com/eu-west-1_AbCdEfGhI,ClientId=1a2b3c4d5e6f7g8h9i0j1k2l3m,ServerSideTokenCheck=false \
      --region eu-west-1
    "IdentityPoolId": "eu-west-1:11111111-2222-3333-4444-555555555555".
  2. Create the IAM role the Identity Pool will hand out for authenticated users. The trust policy is the part the exam cares about — it trusts cognito-identity.amazonaws.com, scoped to this specific Identity Pool via the aud condition.
    cat > trust-policy.json <<'EOF'
    {
      "Version": "2012-10-17",
      "Statement": [{
        "Effect": "Allow",
        "Principal": { "Federated": "cognito-identity.amazonaws.com" },
        "Action": "sts:AssumeRoleWithWebIdentity",
        "Condition": {
          "StringEquals": { "cognito-identity.amazonaws.com:aud": "eu-west-1:11111111-2222-3333-4444-555555555555" },
          "ForAnyValue:StringLike": { "cognito-identity.amazonaws.com:amr": "authenticated" }
        }
      }]
    }
    EOF
    
    aws iam create-role \
      --role-name dva-lab-cognito-authenticated-role \
      --assume-role-policy-document file://trust-policy.json
    
    aws iam attach-role-policy \
      --role-name dva-lab-cognito-authenticated-role \
      --policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
    A role ARN like arn:aws:iam::123456789012:role/dva-lab-cognito-authenticated-role, scoped (for this lab) to read-only S3 access.
  3. Wire the role to the Identity Pool as the default authenticated role.
    aws cognito-identity set-identity-pool-roles \
      --identity-pool-id eu-west-1:11111111-2222-3333-4444-555555555555 \
      --roles authenticated=arn:aws:iam::123456789012:role/dva-lab-cognito-authenticated-role \
      --region eu-west-1
    No output on success — the mapping is now active.
  4. Call GetId, presenting the ID token from Lab 1 as the login (an Identity Pool's --logins map is keyed by the User Pool's issuer path, and expects the ID token there — not the Access token).
    aws cognito-identity get-id \
      --identity-pool-id eu-west-1:11111111-2222-3333-4444-555555555555 \
      --logins cognito-idp.eu-west-1.amazonaws.com/eu-west-1_AbCdEfGhI=${ID_TOKEN} \
      --region eu-west-1
    "IdentityId": "eu-west-1:99999999-aaaa-bbbb-cccc-dddddddddddd" — a Cognito identity, not an AWS credential yet.
  5. Call GetCredentialsForIdentity with that identity ID and the same login map.
    aws cognito-identity get-credentials-for-identity \
      --identity-id "eu-west-1:99999999-aaaa-bbbb-cccc-dddddddddddd" \
      --logins cognito-idp.eu-west-1.amazonaws.com/eu-west-1_AbCdEfGhI=${ID_TOKEN} \
      --region eu-west-1
    A Credentials block with AccessKeyId (starts ASIA...), SecretKey, SessionToken, and an Expiration roughly an hour out — temporary STS credentials, exactly as the guide describes, never a static IAM access key.
  6. Prove they're real AWS credentials scoped to the mapped role by using them directly.
    AWS_ACCESS_KEY_ID="ASIA..." \
    AWS_SECRET_ACCESS_KEY="..." \
    AWS_SESSION_TOKEN="..." \
      aws sts get-caller-identity --region eu-west-1
    "Arn" in the response reads arn:aws:sts::123456789012:assumed-role/dva-lab-cognito-authenticated-role/CognitoIdentityCredentials — proof this session was minted via sts:AssumeRoleWithWebIdentity against the role from Step 2, not via any User Pool mechanism.
⚠️ Why this matters for the exam

This is the single most-tested distinction in the whole module, made physically visible: Lab 1 gave you JWTs that prove identity; this lab exchanges one of those JWTs for temporary AWS credentials that grant access. Same underlying user, two completely different credential types, two completely different AWS services (Cognito User Pools vs. Cognito Identity Pools/STS) doing the work. If you can trace GetIdGetCredentialsForIdentityAssumeRoleWithWebIdentity from memory, the "does this scenario need an Identity Pool" question type stops being guesswork.

Flashcards — 22 Cards

Click card to flip. Mark right or wrong to track score.

Click to reveal answer
1 / 22
Mark:   Score: 0/0

Practice Quiz — 12 Questions

DVA-C02 scenario style, Easy → Specialty. Select an answer to reveal the explanation.

out of 12 correct