I tried resetting TOTP settings by directly calling Amazon Cognito's AdminDeleteSoftwareToken
This page has been translated by machine translation. View original
Introduction
On August 26, 2026, Amazon Cognito added AdminDeleteSoftwareToken, which allows administrators to delete a user's TOTP software token. This API is designed to delete the token of a user who has lost their TOTP device and to reset MFA.
| Item | Before AdminDeleteSoftwareToken was added | After |
|---|---|---|
| Administrator TOTP reset for target user | No dedicated admin API to delete existing TOTP tokens | Can be deleted with AdminDeleteSoftwareToken |
| Recovery while keeping MFA mandatory | Need to consider changing the user pool-wide MFA settings, or deleting and recreating the user | Can delete the target user's token and start the re-setup flow from the MFA_SETUP returned on the next authentication |
| Scope of impact | The entire user pool, or the target user's ID and related data | The target user's software token |
Prerequisites
Verification was performed in ap-northeast-1. After creating a user pool, software token MFA was enabled and MFA was set to required, and an app client and dummy user for password authentication were prepared. Since actual IDs and passwords are not included, variables are used below.
User Pool with Mandatory MFA
First, a user pool for password authentication was created.
USER_POOL_ID=$(aws cognito-idp create-user-pool \
--pool-name totp-reset-test-pool \
--policies '{"PasswordPolicy":{"MinimumLength":8,"RequireUppercase":true,"RequireLowercase":true,"RequireNumbers":true,"RequireSymbols":false}}' \
--query 'UserPool.Id' --output text)
Software token MFA was enabled and the pool's MFA was set to required.
aws cognito-idp set-user-pool-mfa-config \
--user-pool-id "$USER_POOL_ID" \
--mfa-configuration ON \
--software-token-mfa-configuration Enabled=true
App Client and Dummy User
An app client for password authentication was created.
CLIENT_ID=$(aws cognito-idp create-user-pool-client \
--user-pool-id "$USER_POOL_ID" \
--client-name totp-test-client \
--no-generate-secret \
--explicit-auth-flows \
ALLOW_ADMIN_USER_PASSWORD_AUTH \
ALLOW_REFRESH_TOKEN_AUTH \
ALLOW_USER_PASSWORD_AUTH \
--query 'UserPoolClient.ClientId' --output text)
A dummy user was created without sending a notification, and a permanent password was set.
USERNAME=testuser
PASSWORD='<TestUserPassword>'
aws cognito-idp admin-create-user \
--user-pool-id "$USER_POOL_ID" \
--username "$USERNAME" \
--temporary-password "$PASSWORD" \
--message-action SUPPRESS
aws cognito-idp admin-set-user-password \
--user-pool-id "$USER_POOL_ID" \
--username "$USERNAME" \
--password "$PASSWORD" \
--permanent
The dummy user became CONFIRMED after the password was set. Upon authentication, an MFA_SETUP challenge was returned.
TOTP Registration
For the dummy user with a permanent password set, an MFA_SETUP session was obtained and TOTP was registered.
MFA_SETUP_SESSION="$(aws cognito-idp admin-initiate-auth \
--user-pool-id "$USER_POOL_ID" \
--client-id "$CLIENT_ID" \
--auth-flow ADMIN_USER_PASSWORD_AUTH \
--auth-parameters "USERNAME=$USERNAME,PASSWORD=$PASSWORD" \
--query Session --output text)"
ASSOCIATE_JSON="$(aws cognito-idp associate-software-token \
--session "$MFA_SETUP_SESSION" \
--output json)"
TOTP_SECRET="$(printf '%s' "$ASSOCIATE_JSON" | \
python3 -c 'import json,sys; print(json.load(sys.stdin)["SecretCode"])')"
ASSOCIATE_SESSION="$(printf '%s' "$ASSOCIATE_JSON" | \
python3 -c 'import json,sys; print(json.load(sys.stdin)["Session"])')"
To avoid installing oathtool in the local environment, a code was generated using oathtool inside a Docker container. TOTP_SECRET and the generated code are not displayed in standard output.
TOTP_CODE="$(docker run --rm -e "TOTP_SECRET=$TOTP_SECRET" alpine sh -c \
'apk add --no-cache oath-toolkit-oathtool >/dev/null && \
oathtool --totp --base32 "$TOTP_SECRET"')"
aws cognito-idp verify-software-token \
--session "$ASSOCIATE_SESSION" \
--user-code "$TOTP_CODE" \
--query Status --output text
After SUCCESS was returned, it was confirmed that the SOFTWARE_TOKEN_MFA challenge is returned upon authentication.
API Verification
For the user with TOTP registered in the prerequisites, the same items were checked before and after deletion.
Confirmation Before Deletion
Authentication Factors
aws cognito-idp admin-get-user-auth-factors \
--user-pool-id "$USER_POOL_ID" \
--username "$USERNAME" \
--query ConfiguredUserAuthFactors --output json
[
"PASSWORD",
"SOFTWARE_TOKEN"
]
Challenge During Authentication
aws cognito-idp admin-initiate-auth \
--user-pool-id "$USER_POOL_ID" \
--client-id "$CLIENT_ID" \
--auth-flow ADMIN_USER_PASSWORD_AUTH \
--auth-parameters "USERNAME=$USERNAME,PASSWORD=$PASSWORD" \
--query ChallengeName --output text
SOFTWARE_TOKEN_MFA
Password and software token are set as authentication factors, and the TOTP challenge is returned upon authentication.
API Execution
At the time of writing, the latest AWS CLI 2.36.32 and boto3 1.43.81 did not support AdminDeleteSoftwareToken.
Instead of AWS CLI and boto3, botocore was used to add SigV4 signing and call directly.
import json
from urllib.request import Request, urlopen
from botocore.auth import SigV4Auth
from botocore.awsrequest import AWSRequest
from botocore.session import get_session
region = 'ap-northeast-1'
payload = {
'UserPoolId': 'ap-northeast-1_XXXXXXXXX',
'Username': 'testuser',
}
request = AWSRequest(
method='POST',
url=f'https://cognito-idp.{region}.amazonaws.com/',
data=json.dumps(payload, separators=(',', ':')).encode(),
headers={
'Content-Type': 'application/x-amz-json-1.0',
'X-Amz-Target': 'AWSCognitoIdentityProviderService.AdminDeleteSoftwareToken',
},
)
credentials = get_session().get_credentials().get_frozen_credentials()
SigV4Auth(credentials, 'cognito-idp', region).add_auth(request)
prepared = request.prepare()
with urlopen(
Request(prepared.url, data=prepared.body, headers=dict(prepared.headers.items()), method='POST')
) as response:
print(f'HTTP {response.status}')
print(response.read().decode() or '{}')
Confirmation After Deletion
After executing AdminDeleteSoftwareToken, the authentication factors and the challenge during password authentication were confirmed using the same commands as before deletion.
Authentication Factors
[
"PASSWORD"
]
The software token was deleted, and the authentication factor is now password only.
Challenge During Authentication
MFA_SETUP
On the next authentication, the MFA_SETUP challenge was returned, allowing a new software token to be set up.
Summary
For users who use only TOTP as an MFA factor without alternative factors such as SMS MFA, recovery when a device is lost has been a challenge. By using AdminDeleteSoftwareToken, it is possible to implement a recovery flow that resets only the target user's TOTP, returns to MFA_SETUP on the next authentication, and prompts re-registration.
If you have been postponing making MFA mandatory in Cognito due to concerns about recovery when a TOTP device is lost, please try this flow.
Going forward, it would be desirable to see official SDK support as well as APIs that allow administrators to reset authentication factors other than TOTP, including passkeys, on a per-user basis.
