AWS Security ChangesHomeSearch

AWS cognito: MFA example hardened: defensive challenge/device handling and user cleanup

Service: cognito · 2026-10-09 · Documentation low

File: cognito/latest/developerguide/cognito-identity-provider_example_cognito-identity-provider_Scenario_SignUpUserWithMfa_section.md

Summary

The sign-up-with-MFA Python example now reads ChallengeName defensively (handling AuthenticationResult when no MFA challenge is returned), guards against missing NewDeviceMetadata when device tracking is off, returns the created user name from the scenario, and deletes the created user in a finally block with logged cleanup errors.

Security assessment

This is example-code robustness/hygiene: it avoids KeyError crashes and cleans up the test user via admin_delete_user in a finally block. It does not describe or fix a specific vulnerability, boundary, or credential exposure, so it is not security-issue related.

Evidence

+                challenge = response.get("ChallengeName")

Diff

diff --git a/cognito/latest/developerguide/cognito-identity-provider_example_cognito-identity-provider_Scenario_SignUpUserWithMfa_section.md b/cognito/latest/developerguide/cognito-identity-provider_example_cognito-identity-provider_Scenario_SignUpUserWithMfa_section.md
index a4490c7..09946a0 100644
--- a//cognito/latest/developerguide/cognito-identity-provider_example_cognito-identity-provider_Scenario_SignUpUserWithMfa_section.md
+++ b//cognito/latest/developerguide/cognito-identity-provider_example_cognito-identity-provider_Scenario_SignUpUserWithMfa_section.md
@@ -2469 +2469,11 @@ Create a class that runs the scenario. This example also registers an MFA device
-                challenge = response["ChallengeName"]
+                # start_sign_in may return an AuthenticationResult with no challenge
+                # (for example, when the user pool does not require MFA). Read the
+                # challenge defensively and stop if sign-in already succeeded.
+                challenge = response.get("ChallengeName")
+                if challenge is None:
+                    auth_tokens = response.get("AuthenticationResult")
+                    if auth_tokens is not None:
+                        print(f"You're signed in as {user_name}.")
+                        print("Here's your access token:")
+                        pp(auth_tokens["AccessToken"])
+                    break
@@ -2503,0 +2514 @@ Create a class that runs the scenario. This example also registers an MFA device
+                if "NewDeviceMetadata" in auth_tokens:
@@ -2511,2 +2522,19 @@ Create a class that runs the scenario. This example also registers an MFA device
-        device_group_key = auth_tokens["NewDeviceMetadata"]["DeviceGroupKey"]
-        device_key = auth_tokens["NewDeviceMetadata"]["DeviceKey"]
+        # If sign-in returned neither a challenge nor an AuthenticationResult, there
+        # are no tokens to work with; nothing more to do.
+        if auth_tokens is None:
+            return user_name
+    
+        # respond_to_mfa_challenge returns NewDeviceMetadata only when the user pool
+        # has device tracking configured. When it's absent, skip the device-confirmation
+        # and tracked-device sign-in steps instead of failing on a missing key.
+        device_metadata = auth_tokens.get("NewDeviceMetadata")
+        if device_metadata is None:
+            print(
+                "This user pool doesn't track devices, so there's no device to confirm. "
+                "To try the tracked-device sign-in flow, enable device tracking on the "
+                "user pool and run this example again."
+            )
+            print("-" * 88)
+        else:
+            device_group_key = device_metadata["DeviceGroupKey"]
+            device_key = device_metadata["DeviceKey"]
@@ -2515 +2543,3 @@ Create a class that runs the scenario. This example also registers an MFA device
-        print("Let's confirm your MFA device so you don't have re-enter MFA tokens for it.")
+            print(
+                "Let's confirm your MFA device so you don't have to re-enter MFA tokens for it."
+            )
@@ -2542,0 +2573 @@ Create a class that runs the scenario. This example also registers an MFA device
+        return user_name
@@ -2556,0 +2588,2 @@ Create a class that runs the scenario. This example also registers an MFA device
+        client = boto3.client("cognito-idp")
+        created_user = None
@@ -2558 +2591 @@ Create a class that runs the scenario. This example also registers an MFA device
-            run_scenario(boto3.client("cognito-idp"), args.user_pool_id, args.client_id)
+            created_user = run_scenario(client, args.user_pool_id, args.client_id)
@@ -2560,0 +2594,14 @@ Create a class that runs the scenario. This example also registers an MFA device
+        finally:
+            # Clean up the user this scenario created, on success or failure, so a
+            # run never leaves an orphaned user in the pool. Cleanup errors are
+            # logged but do not mask the original outcome.
+            if created_user is not None:
+                try:
+                    client.admin_delete_user(
+                        UserPoolId=args.user_pool_id, Username=created_user
+                    )
+                    print(f"Deleted user {created_user}.")
+                except Exception:
+                    logging.exception(
+                        "Couldn't delete user %s; delete it manually.", created_user
+                    )