Cognito via SDK Best Practices
A working checklist for building on Amazon Cognito - User Pools and Identity Pools - via the SDK, covering token handling, MFA, identity pool scoping, and Lambda trigger hygiene.
Search across all documentation pages
A working checklist for building on Amazon Cognito - User Pools and Identity Pools - via the SDK, covering token handling, MFA, identity pool scoping, and Lambda trigger hygiene.
Each rule is stated positively, with a one-line reason and how to enforce it.
localStorage on the web. It is readable by any script on the page, including a successful XSS payload. Prefer in-memory storage or httpOnly, secure cookies set by your backend.GlobalSignOut (or the admin equivalent) on explicit logout. It revokes all outstanding refresh tokens for that user, not just the local session's copy.# --- Python (boto3) ---
# Good: revoke every refresh token on explicit logout.
import boto3
cognito = boto3.client("cognito-idp")
cognito.global_sign_out(AccessToken=access_token)// --- TypeScript (AWS SDK v3) ---
// Good: revoke every refresh token on explicit logout.
import { CognitoIdentityProviderClient, GlobalSignOutCommand } from "@aws-sdk/client-cognito-identity-provider";
const cognito = new CognitoIdentityProviderClient({});
await cognito.send(new GlobalSignOutCommand({ AccessToken: accessToken }));VerifySoftwareToken before enabling MFA. Never call SetUserMFAPreference to enable a factor the user hasn't proven they can actually use.AdminSetUserMFAPreference disable flow gated by your own identity check, or they're locked out permanently.MfaConfiguration: "ON" is stronger than relying on every user opting in themselves.ChallengeName as expected, not an error. Client code should branch on InitiateAuth responses cleanly rather than treating a challenge as a failure path.${cognito-identity.amazonaws.com:sub} so each user's credentials only reach their own resources.ServerSideTokenCheck on linked user pool providers. Without it, a disabled or deleted account's still-valid JWT can keep exchanging for AWS credentials.AllowUnauthenticatedIdentities deliberately, not by default. Decide explicitly whether guest AWS access is needed; leaving it on unintentionally widens your attack surface.{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ScopedToOwnPrefix",
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject"],
"Resource": "arn:aws:s3:::acme-uploads/${cognito-identity.amazonaws.com:sub}/*"
}
]
}PreSignUp, CreateAuthChallenge, and friends run synchronously in the sign-up/sign-in path; a slow call inside one directly slows down every affected user.PostConfirmation/PostAuthentication async paths, not from a trigger the user is waiting on.CreateAuthChallenge function that sends OTP emails needs ses:SendEmail, not broad service access.DefineAuthChallenge for custom auth. Without an explicit attempt limit, a client can loop challenge/response indefinitely.GenerateSecret unset for those clients.CallbackURLs per environment. The Hosted UI rejects any redirect URI that isn't an exact match, including trailing slashes - keep dev/staging/prod URLs separate and precise.PostConfirmation, since Apple will not send it again on subsequent sign-ins.ExplicitAuthFlows to only what each client needs. Don't enable USER_PASSWORD_AUTH on a public client if USER_SRP_AUTH (or Hosted UI) is sufficient.Shorter tokens narrow the exposure window if one is stolen. Session length is better handled by a longer-lived refresh token, which can be revoked independently via GlobalSignOut.
Any script running on the page, including an XSS payload, can read localStorage. In-memory storage or httpOnly, secure cookies set server-side are much harder for a malicious script to exfiltrate.
Yes, as a fallback for users without an authenticator app, but TOTP should be the default given its lower cost and immunity to SIM-swap and SMS interception risks.
Scoping the mapped role with policy variables (like ${cognito-identity.amazonaws.com:sub}) so each user's temporary credentials can only touch their own data, not everyone's.
Without it, a JWT that's still cryptographically valid but belongs to a disabled or deleted user pool account can keep being exchanged for AWS credentials through the identity pool.
Fast enough that the user doesn't notice, since they run synchronously in the sign-up/sign-in request path. Move anything non-blocking, like sending a welcome email, to a trigger later in the lifecycle.
No. A secret embedded in a browser or mobile binary isn't secret. Leave GenerateSecret unset for any client running outside a trusted backend.
It's effectively lost. Apple sends the user's full name and email only on the very first authorization; capture it in PostConfirmation or you won't see it again without the user revoking and re-granting access.
Stack versions: This page was written for boto3 1.43.x (Python 3.10+) and the AWS SDK for JavaScript v3 (Node.js 18+).
Reviewed by Chris St. John·Last updated Jul 25, 2026