How to Fix IAM MFA Token Out of Sync: Resolving MultiFactorAuthentication Failed Errors
Threat/Impact Level: HIGH | Exploitability/Downtime Risk: HIGH | Time to Fix: 5–15 mins
TL;DR
- What broke: Your virtual MFA device (Google Authenticator, Authy, etc.) clock has drifted beyond AWS's ±30-second resync window, or the serial number ARN in your policy/config is mismatched — every
sts:AssumeRoleor console login requiring MFA is now hard-blocked. - How to fix it: Force-resync the MFA device via the IAM console or AWS CLI using two consecutive TOTP codes, or re-associate the device if the seed is corrupted.
- Use our Client-Side Sandbox above to paste your IAM policy or CLI error and auto-generate the corrected resync commands and policy ARN references.
The Incident (What Does the Error Mean?)
Raw error output from AWS CLI or SDK:
An error occurred (MultiFactorAuthentication) when calling the AssumeRole operation:
MultiFactorAuthentication failed with invalid MFA one time pass code.
Or on console login:
Your authentication information is incorrect. Please try again.
(MFA token validation failed — code already used or out of sync)
Immediate consequence: The IAM principal cannot authenticate. If this principal is a human operator during an incident, they are locked out of the AWS console and CLI entirely. If it's a CI/CD role with sts:AssumeRole requiring MFA (common in cross-account setups), your entire deployment pipeline is dead.
The Attack Vector / Blast Radius
This failure mode has two distinct blast radii:
1. Operational Lockout (Primary Risk) AWS enforces a strict TOTP window. Clock drift >60 seconds on the device running your authenticator app means every generated code is invalid before it's even submitted. In a production incident, a locked-out on-call engineer cannot access CloudWatch, EC2, or RDS. Mean time to recovery balloons.
2. Mismatched Serial ARN (Latent Misconfiguration)
If your IAM policy Condition block references the wrong MFA device ARN — e.g., after a device was deleted and re-enrolled — AWS rejects the token even if the code itself is valid. This is a silent misconfiguration that only surfaces under MFA-required conditions:
"Condition": {
"StringEquals": {
"aws:MultiFactorAuthPresent": "true"
},
"ArnLike": {
"aws:MultiFactorAuthAge": "arn:aws:iam::123456789012:mfa/old-device-name"
}
}
This condition silently blocks all role assumptions even with a valid, synced token.
3. Replay / Reuse Attack Surface AWS rejects a TOTP code that has already been used within the same 30-second window. Automated scripts that retry on failure can burn through valid codes, extending the lockout window. Never retry MFA submission in a tight loop.
How to Fix It (The Solution)
Basic Fix: Resync the Virtual MFA Device
AWS allows a one-time resync using two consecutive valid TOTP codes (from two different 30-second windows).
Via AWS CLI:
- # Wrong: Submitting a single code or retrying the same code
- aws iam enable-mfa-device \
- --user-name john.doe \
- --serial-number arn:aws:iam::123456789012:mfa/john.doe \
- --authentication-code-1 123456
+ # Correct: Resync using two consecutive codes (wait for the next 30s window for code2)
+ aws iam resync-mfa-device \
+ --user-name john.doe \
+ --serial-number arn:aws:iam::123456789012:mfa/john.doe \
+ --authentication-code-1 123456 \
+ --authentication-code-2 789012
⚠️
authentication-code-1andauthentication-code-2must be from two consecutive 30-second TOTP windows. Generate code1, wait for the authenticator to cycle, then immediately submit code2.
Fix clock drift on the device (Android/iOS):
- Google Authenticator: Settings → Time correction for codes → Sync now
- Authy: Ensure device NTP sync is enabled at OS level
- Linux VM running soft token:
sudo timedatectl set-ntp true && sudo chronyc makestep
Enterprise Best Practice: Validate MFA ARN in Policy Conditions and Enforce via SCPs
The aws:MultiFactorAuthAge condition with a hardcoded ARN is a common drift point. Use aws:MultiFactorAuthPresent with age-based controls instead, and audit serial ARN references in all trust policies.
Broken trust policy (hardcoded, stale device ARN):
- {
- "Effect": "Allow",
- "Principal": {"AWS": "arn:aws:iam::123456789012:root"},
- "Action": "sts:AssumeRole",
- "Condition": {
- "ArnLike": {
- "aws:MultiFactorAuthAge": "arn:aws:iam::123456789012:mfa/old-device"
- }
- }
- }
+ {
+ "Effect": "Allow",
+ "Principal": {"AWS": "arn:aws:iam::123456789012:root"},
+ "Action": "sts:AssumeRole",
+ "Condition": {
+ "Bool": {
+ "aws:MultiFactorAuthPresent": "true"
+ },
+ "NumericLessThan": {
+ "aws:MultiFactorAuthAge": "3600"
+ }
+ }
+ }
This enforces MFA presence and a max token age of 1 hour — without binding to a specific device ARN that can go stale.
Re-associate a broken/corrupted MFA device:
- # Attempting to reuse a deactivated device
- aws iam enable-mfa-device --user-name john.doe \
- --serial-number arn:aws:iam::123456789012:mfa/john.doe \
- --authentication-code-1 000000 --authentication-code-2 000001
+ # Step 1: Deactivate the broken device first
+ aws iam deactivate-mfa-device \
+ --user-name john.doe \
+ --serial-number arn:aws:iam::123456789012:mfa/john.doe
+
+ # Step 2: Delete the virtual device
+ aws iam delete-virtual-mfa-device \
+ --serial-number arn:aws:iam::123456789012:mfa/john.doe
+
+ # Step 3: Create new virtual device and re-enroll
+ aws iam create-virtual-mfa-device \
+ --virtual-mfa-device-name john.doe \
+ --outfile /tmp/qr.png \
+ --bootstrap-method QRCodePNG
💡 Tired of pasting proprietary configs into ChatGPT? Generic AI tools log your company's ARNs, DB strings, and private keys. StackEngine is a zero-backend, pure Client-Side WASM utility. Drop your failing config into the sandbox above. We redact your secrets locally in the browser and auto-generate the refactored code using your own API key.
Prevention in CI/CD
1. Checkov: Detect hardcoded MFA device ARNs in Terraform IAM policies
Add a custom Checkov check or use checkov -d . — flag any aws:MultiFactorAuthAge condition using ArnLike instead of Bool/NumericLessThan.
2. AWS Config Rule: Enforce MFA on all IAM users
+ resource "aws_config_rule" "mfa_enabled" {
+ name = "iam-user-mfa-enabled"
+ source {
+ owner = "AWS"
+ source_identifier = "IAM_USER_MFA_ENABLED"
+ }
+ }
3. SCP: Block sts:AssumeRole without MFA at the Org level
{
"Effect": "Deny",
"Action": "sts:AssumeRole",
"Resource": "*",
"Condition": {
"BoolIfExists": {
"aws:MultiFactorAuthPresent": "false"
}
}
}
4. NTP enforcement on all hosts running soft tokens
In your AMI bootstrap or Ansible playbook:
- # No NTP enforcement — clock drift accumulates silently
+ - name: Ensure chrony is running and synced
+ service:
+ name: chronyd
+ state: started
+ enabled: true
+
+ - name: Force immediate time sync
+ command: chronyc makestep
5. Rotate and audit MFA device ARNs in trust policies as part of IAM user offboarding runbooks. A stale ARN in a role trust policy is a silent lockout waiting to happen.