User Pools: Sign-Up, Sign-In & MFA
A Cognito user pool manages a user from their first SignUp call through every sign-in for the life of their account.
Busque em todas as páginas da documentação
A Cognito user pool manages a user from their first SignUp call through every sign-in for the life of their account.
This page walks the full lifecycle - registration, confirmation, password sign-in, and adding TOTP-based multi-factor authentication - as one connected recipe rather than isolated calls.
A user's life in a pool starts unconfirmed, becomes confirmed after a code check, and then signs in repeatedly via InitiateAuth.
MFA adds a second factor on top of that sign-in. Cognito supports software token (TOTP, e.g. Google Authenticator) and SMS-based MFA; this page focuses on TOTP, the recommended option since SMS delivery can be intercepted or delayed.
Setting up TOTP MFA is a two-step handshake: AssociateSoftwareToken generates a shared secret, and VerifySoftwareToken confirms the user entered a valid code from their authenticator app. Only after that does SetUserMFAPreference turn MFA on for future sign-ins.
Quick-reference recipe card - copy-paste ready.
# --- Python (boto3) ---
import boto3
cognito = boto3.client("cognito-idp")
# Step 1: sign in. If MFA is enabled, tokens are NOT returned yet.
resp = cognito.initiate_auth(
ClientId=CLIENT_ID,
AuthFlow="USER_PASSWORD_AUTH",
AuthParameters={"USERNAME": "jane@example.com", "PASSWORD": "Str0ng!Passw0rd"},
)
if resp.get("ChallengeName") == "SOFTWARE_TOKEN_MFA":
tokens = cognito.respond_to_auth_challenge(
ClientId=CLIENT_ID,
ChallengeName="SOFTWARE_TOKEN_MFA",
Session=resp["Session"],
ChallengeResponses={"USERNAME": "jane@example.com", "SOFTWARE_TOKEN_MFA_CODE": "123456"},
)["AuthenticationResult"]// --- TypeScript (AWS SDK v3) ---
import {
CognitoIdentityProviderClient, InitiateAuthCommand, RespondToAuthChallengeCommand,
} from "@aws-sdk/client-cognito-identity-provider";
const cognito = new CognitoIdentityProviderClient({});
// Step 1: sign in. If MFA is enabled, tokens are NOT returned yet.
const resp = await cognito.send(new InitiateAuthCommand({
ClientId: CLIENT_ID,
AuthFlow: "USER_PASSWORD_AUTH",
AuthParameters: { USERNAME: "jane@example.com", PASSWORD: "Str0ng!Passw0rd" },
}));
if (resp.ChallengeName === "SOFTWARE_TOKEN_MFA") {
const { AuthenticationResult } = await cognito.send(new RespondToAuthChallengeCommand({
ClientId: CLIENT_ID,
ChallengeName: "SOFTWARE_TOKEN_MFA",
Session: resp.Session,
ChallengeResponses: { USERNAME: "jane@example.com", SOFTWARE_TOKEN_MFA_CODE: "123456" },
}));
}When to reach for this:
Enroll a signed-in user in TOTP MFA end to end: associate a software token, verify it, then make it the required factor.
# --- Python (boto3) ---
import boto3
cognito = boto3.client("cognito-idp")
# 1. Generate a shared TOTP secret for this user's session.
assoc = cognito.associate_software_token(AccessToken=access_token)
secret = assoc["SecretCode"] # show as a QR code / manual entry key
# 2. User enters the current 6-digit code from their authenticator app.
cognito.verify_software_token(
AccessToken=access_token,
UserCode="654321",
)
# 3. Make TOTP the preferred, required MFA method going forward.
cognito.set_user_mfa_preference(
AccessToken=access_token,
SoftwareTokenMfaSettings={"Enabled": True, "PreferredMfa": True},
)// --- TypeScript (AWS SDK v3) ---
import {
CognitoIdentityProviderClient, AssociateSoftwareTokenCommand,
VerifySoftwareTokenCommand, SetUserMFAPreferenceCommand,
} from "@aws-sdk/client-cognito-identity-provider";
const cognito = new CognitoIdentityProviderClient({});
// 1. Generate a shared TOTP secret for this user's session.
const assoc = await cognito.send(new AssociateSoftwareTokenCommand({ AccessToken: accessToken }));
const secret = assoc.SecretCode; // show as a QR code / manual entry key
// 2. User enters the current 6-digit code from their authenticator app.
await cognito.send(new VerifySoftwareTokenCommand({
AccessToken: accessToken,
UserCode: "654321",
}));
// 3. Make TOTP the preferred, required MFA method going forward.
await cognito.send(new SetUserMFAPreferenceCommand({
AccessToken: accessToken,
SoftwareTokenMfaSettings: { Enabled: true, PreferredMfa: true },
}));What this demonstrates:
AssociateSoftwareToken returns a SecretCode you render as a QR code (otpauth:// URI) or a manual-entry string.VerifySoftwareToken proves the user actually scanned/entered the secret correctly before you rely on it.SetUserMFAPreference is what actually turns MFA on for sign-in; the earlier two steps only prepare the token.UNCONFIRMED status and (if configured) triggers a verification code.CONFIRMED.InitiateAuth returned a ChallengeName (MFA, or a required password change on first login).InitiateAuth (and the MFA challenge, if required) - the pool does not remember previous MFA success.| Flow | How the password travels | Typical use |
|---|---|---|
USER_PASSWORD_AUTH | Sent to Cognito directly, over TLS | Server-side backends, simpler client code |
USER_SRP_AUTH | Never transmitted - Secure Remote Password protocol proves knowledge of it | Public clients (mobile, browser) preferring not to send the raw password anywhere |
USER_SRP_AUTH requires the client to compute the SRP handshake; the official Amplify and Cognito Identity SDKs implement this for you, while raw boto3/SDK v3 calls generally use USER_PASSWORD_AUTH for simplicity in trusted backend contexts.
When InitiateAuth returns a ChallengeName of SOFTWARE_TOKEN_MFA, it also returns a Session string - an opaque token tying the challenge to that specific sign-in attempt. You must pass that same Session value into RespondToAuthChallenge, along with the six-digit code. A stale or reused Session value fails with an expired-session error, since each Session is single-use and time-limited.
Session string - Session values expire after a few minutes. Fix: if the challenge is not completed promptly, restart from InitiateAuth.AssociateSoftwareToken, VerifySoftwareToken, and SetUserMFAPreference all require the access token, not the ID token. Fix: always pass AuthenticationResult.AccessToken.AdminSetUserMFAPreference disable flow, gated by your own identity verification.ExplicitAuthFlows on the app client - InitiateAuth fails immediately with NotAuthorizedException if the flow isn't allowed on that client. Fix: include ALLOW_USER_PASSWORD_AUTH or ALLOW_USER_SRP_AUTH when creating the client.SetUserMFAPreference as optional after verification - skipping it means the verified token exists but MFA is never actually enforced at sign-in. Fix: always call it as the final step.| Alternative | Use When | Don't Use When |
|---|---|---|
| TOTP (software token) MFA | Default choice for most apps | Users have no smartphone/authenticator access |
| SMS MFA | Users without authenticator apps, low-volume | High-volume apps sensitive to per-SMS cost or SIM-swap risk |
| Cognito Hosted UI | Want built-in, pre-built sign-in/MFA pages fast | Need a fully custom-branded sign-in experience |
| Passwordless custom auth (Lambda triggers) | Want OTP-over-email/SMS instead of passwords entirely | Team isn't ready to own custom Lambda challenge logic |
SignUp, then ConfirmSignUp with the verification code, then InitiateAuth to sign in. If MFA is enabled, RespondToAuthChallenge follows InitiateAuth.
The access token, obtained from a completed sign-in's AuthenticationResult.AccessToken. The ID token will not work for AssociateSoftwareToken, VerifySoftwareToken, or SetUserMFAPreference.
No. It returns a ChallengeName (e.g. SOFTWARE_TOKEN_MFA) and a Session string instead. Tokens are only issued after RespondToAuthChallenge succeeds.
USER_PASSWORD_AUTH sends the password to Cognito over TLS. USER_SRP_AUTH uses the Secure Remote Password protocol so the raw password is never transmitted at all.
Call SetUserMFAPreference with SoftwareTokenMfaSettings: { Enabled: true, PreferredMfa: true } after VerifySoftwareToken succeeds. Association and verification alone do not enforce MFA at sign-in.
There is no self-service recovery without another verified factor. Build an admin-side flow using AdminSetUserMFAPreference to disable MFA after your own out-of-band identity check.
A few minutes. It is single-use and tied to that specific challenge; a delayed or reused Session fails and requires restarting from InitiateAuth.
No. SMS delivery is billed through SNS on a per-message basis and carries carrier-dependent delivery risk. TOTP has no per-code delivery cost and is generally recommended as the default.
Yes, a pool can support both methods, and SetUserMFAPreference lets you mark one as PreferredMfa while leaving the other enabled as a fallback.
Stack versions: This page was written for boto3 1.43.x (Python 3.10+) and the AWS SDK for JavaScript v3 (Node.js 18+).
Revisado por Chris St. John·Última atualização: 25 de jul. de 2026