aws-amplify / amplify-js

A declarative JavaScript library for application development using cloud services.
https://docs.amplify.aws/lib/q/platform/js
Apache License 2.0
9.44k stars 2.13k forks source link

Cognito user deleting shortly after signup #10374

Closed hope-portal-services closed 2 years ago

hope-portal-services commented 2 years ago

Before opening, please confirm:

JavaScript Framework

React

Amplify APIs

Authentication

Amplify Categories

auth

Environment information

``` # Put output below this line System: OS: macOS 12.5.1 CPU: (8) x64 Apple M2 Memory: 37.53 MB / 8.00 GB Shell: 3.2.57 - /bin/bash Binaries: Node: 12.14.0 - ~/.nvm/versions/node/v12.14.0/bin/node npm: 6.13.4 - ~/.nvm/versions/node/v12.14.0/bin/npm Browsers: Chrome: 105.0.5195.125 Firefox: 71.0 Safari: 15.6.1 npmGlobalPackages: npm: 6.13.4 ```

Describe the bug

Sporadic issue: Succeeds 75% of the time, failure 25% of the time

  1. User registers using email/password combination
  2. The unconfirmed user is now in the user pool with an unverified email - (confirmed, duplicable)
  3. Verification code is sent to verify the Email Address
  4. Between 1-10 seconds elapses
  5. The Cognito user is now deleted from the user pool

Expected behavior

  1. User registers using email/password combination
  2. The unconfirmed user is now in the user pool with an unverified email
  3. Verification code is sent to verify the Email Address
  4. Between 1-10 seconds elapses
  5. The Cognito user is still in the user pool
  6. The user is able to enter the verification code and confirm their user

Reproduction steps

The issue is sporadic, I have not been able to reliably duplicate.

Code Snippet

// Put your code below this line.
export const registerClient = (details) => async (dispatch) => {
  let user = { email: (details.email).toLowerCase(), password: details.password, first: details.first_name, last: details.last_name };
  const { email, password, first, last } = user;
  if (email && password && first && last) {
    let attributes = {
      "email": ((email).toLowerCase()).replace(/\s+/g, ""),
      "name": `${first} ${last}`
    };
    if (details.home_phone) attributes.phone_number = formatUSPhoneNumber(details.home_phone);
    return Auth.signUp({
      username: ((email).toLowerCase()).replace(/\s+/g, ""),
      password,
      attributes
    })
    .then((cognitoUser) => {
      LogRocket.track(`Cognito user signed up. - ${first} ${last} - ${email}`);
      dispatch(logEvent("user creation", { first_name: first, last_name: last, email, cognito_id: cognitoUser.userSub }));
      dispatch({ type: UPDATE_CLIENT_REGISTRATION, payload: { collector: "registration_config", key: "cognito_id", value: cognitoUser.userSub } });
      dispatch({ type: UPDATE_CLIENT_REGISTRATION, payload: { collector: "registration_config", key: "is_verifying", value: true } });
      dispatch({ type: IS_SIGNING_UP, payload: true });
      return { success: true };
    })
    .catch((error) => {
      dispatch(showNotification("error", "Cognito signup", error.message));
      return { success: false };
    });
  } else {
    dispatch(showNotification("error", "Missing signup credentials", "You must enter a username and password."));
    return { success: false };
  }
};

Log output

``` // Put your logs below this line VM12:2491 Symbol.observable as defined by Redux and Redux DevTools do not match. This could cause your app to behave differently if the DevTools are not loaded. Consider polyfilling Symbol.observable before Redux is imported or avoid polyfilling Symbol.observable altogether. M @ VM12:2491 build.umd.js:2977 [DEBUG] 51:20.302 Amplify - amplify config Object build.umd.js:2977 [DEBUG] 51:20.303 AuthClass - configure Auth build.umd.js:2977 [DEBUG] 51:20.303 Parser - parse config Array(3) build.umd.js:2977 [DEBUG] 51:20.303 Hub - Dispatching to auth with Object build.umd.js:2977 [DEBUG] 51:20.303 RestAPI - configure Rest API Object build.umd.js:2977 [DEBUG] 51:20.304 RestAPI - create Rest API instance build.umd.js:2977 [DEBUG] 51:20.304 RestClient - API Options Object build.umd.js:2977 [DEBUG] 51:20.304 I18n - configure I18n build.umd.js:2977 [DEBUG] 51:20.304 I18n - create I18n instance build.umd.js:2977 [DEBUG] 51:20.304 PubSub - configure PubSub Object build.umd.js:2977 [DEBUG] 51:20.304 GraphQLAPI - configure GraphQL API Object build.umd.js:2977 [DEBUG] 51:20.304 GraphQLAPI - create Rest instance build.umd.js:2977 [DEBUG] 51:20.304 RestClient - API Options Object build.umd.js:2977 [DEBUG] 51:20.304 RestAPI - configure Rest API Object build.umd.js:2977 [DEBUG] 51:20.304 RestAPI - create Rest API instance build.umd.js:2977 [DEBUG] 51:20.304 RestClient - API Options Object build.umd.js:2977 [DEBUG] 51:20.304 GraphQLAPI - configure GraphQL API Object build.umd.js:2977 [DEBUG] 51:20.304 GraphQLAPI - create Rest instance build.umd.js:2977 [DEBUG] 51:20.304 RestClient - API Options Object build.umd.js:2977 [DEBUG] 51:20.304 AuthClass - configure Auth build.umd.js:2977 [DEBUG] 51:20.304 Parser - parse config Array(3) build.umd.js:2977 [DEBUG] 51:20.304 Hub - Dispatching to auth with Object build.umd.js:2977 [DEBUG] 51:20.707 AuthClass - Getting current session build.umd.js:2977 [DEBUG] 51:20.708 AuthClass - Failed to get user from user pool build.umd.js:2977 [DEBUG] 51:20.708 AuthClass - Failed to get the current user No current user build.umd.js:2977 [DEBUG] 51:20.712 RestClient - GET Object build.umd.js:2977 [DEBUG] 51:20.713 Credentials - getting credentials build.umd.js:2977 [DEBUG] 51:20.713 Credentials - picking up credentials build.umd.js:2977 [DEBUG] 51:20.713 Credentials - getting new cred promise build.umd.js:2977 [DEBUG] 51:20.713 Credentials - checking if credentials exists and not expired build.umd.js:2977 [DEBUG] 51:20.713 Credentials - need to get a new credential or refresh the existing one build.umd.js:2977 [DEBUG] 51:20.713 Credentials - no credentials for expiration check build.umd.js:2977 [DEBUG] 51:20.714 AuthClass - Getting current user credentials build.umd.js:2977 [DEBUG] 51:20.714 AuthClass - Getting current session build.umd.js:2977 [DEBUG] 51:20.714 AuthClass - Failed to get user from user pool build.umd.js:2977 [DEBUG] 51:20.714 AuthClass - Failed to get the current user No current user build.umd.js:2977 [DEBUG] 51:20.714 AuthClass - getting session failed No current user build.umd.js:2977 [DEBUG] 51:20.714 Credentials - setting credentials for guest build.umd.js:2977 [DEBUG] 51:20.714 RestClient - No credentials available, the request will be unsigned build.umd.js:2977 [Meta Pixel] - Removed URL query parameters due to potential violations. e. @ build.umd.js:2977 build.umd.js:2977 [DEBUG] 51:21.815 RestClient - GET Object build.umd.js:2977 [DEBUG] 51:21.816 Credentials - getting credentials build.umd.js:2977 [DEBUG] 51:21.816 Credentials - picking up credentials build.umd.js:2977 [DEBUG] 51:21.816 Credentials - getting new cred promise build.umd.js:2977 [DEBUG] 51:21.816 Credentials - checking if credentials exists and not expired build.umd.js:2977 [DEBUG] 51:21.816 Credentials - need to get a new credential or refresh the existing one build.umd.js:2977 [DEBUG] 51:21.816 Credentials - no credentials for expiration check build.umd.js:2977 [DEBUG] 51:21.816 AuthClass - Getting current user credentials build.umd.js:2977 [DEBUG] 51:21.816 AuthClass - Getting current session build.umd.js:2977 [DEBUG] 51:21.816 AuthClass - Failed to get user from user pool build.umd.js:2977 [DEBUG] 51:21.816 AuthClass - Failed to get the current user No current user build.umd.js:2977 [DEBUG] 51:21.817 AuthClass - getting session failed No current user build.umd.js:2977 [DEBUG] 51:21.817 Credentials - setting credentials for guest build.umd.js:2977 [DEBUG] 51:21.817 RestClient - No credentials available, the request will be unsigned build.umd.js:2977 [DEBUG] 51:22.949 RestClient - POST Object build.umd.js:2977 [DEBUG] 51:22.949 Credentials - getting credentials build.umd.js:2977 [DEBUG] 51:22.949 Credentials - picking up credentials build.umd.js:2977 [DEBUG] 51:22.949 Credentials - getting new cred promise build.umd.js:2977 [DEBUG] 51:22.949 Credentials - checking if credentials exists and not expired build.umd.js:2977 [DEBUG] 51:22.949 Credentials - need to get a new credential or refresh the existing one build.umd.js:2977 [DEBUG] 51:22.949 Credentials - no credentials for expiration check build.umd.js:2977 [DEBUG] 51:22.950 AuthClass - Getting current user credentials build.umd.js:2977 [DEBUG] 51:22.953 AuthClass - Getting current session build.umd.js:2977 [DEBUG] 51:22.953 AuthClass - Failed to get user from user pool build.umd.js:2977 [DEBUG] 51:22.953 AuthClass - Failed to get the current user No current user build.umd.js:2977 [DEBUG] 51:22.953 AuthClass - getting session failed No current user build.umd.js:2977 [DEBUG] 51:22.953 Credentials - setting credentials for guest build.umd.js:2977 [DEBUG] 51:22.953 RestClient - No credentials available, the request will be unsigned build.umd.js:2977 [DEBUG] 51:25.447 RestClient - PATCH Object build.umd.js:2977 [DEBUG] 51:25.447 Credentials - getting credentials build.umd.js:2977 [DEBUG] 51:25.447 Credentials - picking up credentials build.umd.js:2977 [DEBUG] 51:25.447 Credentials - getting new cred promise build.umd.js:2977 [DEBUG] 51:25.447 Credentials - checking if credentials exists and not expired build.umd.js:2977 [DEBUG] 51:25.447 Credentials - need to get a new credential or refresh the existing one build.umd.js:2977 [DEBUG] 51:25.447 Credentials - no credentials for expiration check build.umd.js:2977 [DEBUG] 51:25.447 AuthClass - Getting current user credentials build.umd.js:2977 [DEBUG] 51:25.451 AuthClass - Getting current session build.umd.js:2977 [DEBUG] 51:25.451 AuthClass - Failed to get user from user pool build.umd.js:2977 [DEBUG] 51:25.451 AuthClass - Failed to get the current user No current user build.umd.js:2977 [DEBUG] 51:25.452 AuthClass - getting session failed No current user build.umd.js:2977 [DEBUG] 51:25.452 Credentials - setting credentials for guest build.umd.js:2977 [DEBUG] 51:25.452 RestClient - No credentials available, the request will be unsigned build.umd.js:2977 [DEBUG] 51:32.93 RestClient - PATCH Object build.umd.js:2977 [DEBUG] 51:32.94 Credentials - getting credentials build.umd.js:2977 [DEBUG] 51:32.94 Credentials - picking up credentials build.umd.js:2977 [DEBUG] 51:32.94 Credentials - getting new cred promise build.umd.js:2977 [DEBUG] 51:32.94 Credentials - checking if credentials exists and not expired build.umd.js:2977 [DEBUG] 51:32.94 Credentials - need to get a new credential or refresh the existing one build.umd.js:2977 [DEBUG] 51:32.94 Credentials - no credentials for expiration check build.umd.js:2977 [DEBUG] 51:32.94 AuthClass - Getting current user credentials build.umd.js:2977 [DEBUG] 51:32.98 AuthClass - Getting current session build.umd.js:2977 [DEBUG] 51:32.98 AuthClass - Failed to get user from user pool build.umd.js:2977 [DEBUG] 51:32.98 AuthClass - Failed to get the current user No current user build.umd.js:2977 [DEBUG] 51:32.98 AuthClass - getting session failed No current user build.umd.js:2977 [DEBUG] 51:32.98 Credentials - setting credentials for guest build.umd.js:2977 [DEBUG] 51:32.98 RestClient - No credentials available, the request will be unsigned client-registration:1 [DOM] Input elements should have autocomplete attributes (suggested: "new-password"): (More info: https://goo.gl/9p2vKq) ​ client-registration:1 [DOM] Input elements should have autocomplete attributes (suggested: "new-password"): (More info: https://goo.gl/9p2vKq) ​ build.umd.js:2977 [DEBUG] 51:42.895 AuthClass - signUp attrs: Array(3) build.umd.js:2977 [DEBUG] 51:42.896 AuthClass - signUp validation data: null build.umd.js:2977 [DEBUG] 51:43.226 Hub - Dispatching to auth with Object build.umd.js:2977 LogRocket Track API: Ignored unsupported type (undefined) at property plan_id. Expected one of: boolean,number,string,boolean[],number[],string[] e. @ build.umd.js:2977 build.umd.js:2977 [DEBUG] 51:43.238 RestClient - PATCH Object build.umd.js:2977 [DEBUG] 51:43.239 Credentials - getting credentials build.umd.js:2977 [DEBUG] 51:43.239 Credentials - picking up credentials build.umd.js:2977 [DEBUG] 51:43.239 Credentials - getting new cred promise build.umd.js:2977 [DEBUG] 51:43.239 Credentials - checking if credentials exists and not expired build.umd.js:2977 [DEBUG] 51:43.239 Credentials - need to get a new credential or refresh the existing one build.umd.js:2977 [DEBUG] 51:43.239 Credentials - no credentials for expiration check build.umd.js:2977 [DEBUG] 51:43.239 AuthClass - Getting current user credentials build.umd.js:2977 [DEBUG] 51:43.239 AuthClass - Getting current session build.umd.js:2977 [DEBUG] 51:43.239 AuthClass - Failed to get user from user pool build.umd.js:2977 [DEBUG] 51:43.239 AuthClass - Failed to get the current user No current user build.umd.js:2977 [DEBUG] 51:43.240 AuthClass - getting session failed No current user build.umd.js:2977 [DEBUG] 51:43.240 Credentials - setting credentials for guest build.umd.js:2977 [DEBUG] 51:43.240 RestClient - No credentials available, the request will be unsigned DevTools failed to load source map: Could not load content for chrome-extension://fheoggkfdfchfphceeifdbepaooicaho/sourceMap/chrome/scripts/iframe_form_check.map: System error: net::ERR_BLOCKED_BY_CLIENT DevTools failed to load source map: Could not load content for chrome-extension://fheoggkfdfchfphceeifdbepaooicaho/sourceMap/chrome/scripts/iframe_form_check.map: System error: net::ERR_BLOCKED_BY_CLIENT DevTools failed to load source map: Could not load content for chrome-extension://fheoggkfdfchfphceeifdbepaooicaho/sourceMap/chrome/scripts/iframe_form_check.map: System error: net::ERR_BLOCKED_BY_CLIENT DevTools failed to load source map: Could not load content for chrome-extension://fheoggkfdfchfphceeifdbepaooicaho/sourceMap/chrome/scripts/iframe_form_check.map: System error: net::ERR_BLOCKED_BY_CLIENT DevTools failed to load source map: Could not load content for chrome-extension://fheoggkfdfchfphceeifdbepaooicaho/sourceMap/chrome/scripts/content_scroll_mid_detection.map: System error: net::ERR_BLOCKED_BY_CLIENT menu.js:4 [Violation] Added non-passive event listener to a scroll-blocking 'wheel' event. Consider marking event handler as 'passive' to make the page more responsive. See https://www.chromestatus.com/feature/5745543795965952 P @ VM30 menu.js:4 m @ VM30 menu.js:9 J @ VM30 menu.js:4 m @ VM30 menu.js:9 J @ VM30 menu.js:4 m @ VM30 menu.js:9 p @ VM30 menu.js:9 F @ VM30 menu.js:4 N @ VM30 menu.js:4 build.umd.js:1233 POST https://cognito-idp.us-east-1.amazonaws.com/ 400 (anonymous) @ build.umd.js:1233 Promise.then (async) o @ build.umd.js:1227 (anonymous) @ build.umd.js:1277 (anonymous) @ instrument.ts:153 (anonymous) @ instrument.ts:153 t.request @ Client.js:123 t.confirmRegistration @ CognitoUser.js:697 (anonymous) @ Auth.ts:424 e.confirmSignUp @ Auth.ts:423 (anonymous) @ client-registration.js:72 l @ runtime.js:63 (anonymous) @ runtime.js:294 (anonymous) @ runtime.js:119 n @ asyncToGenerator.js:3 o @ asyncToGenerator.js:25 (anonymous) @ asyncToGenerator.js:32 (anonymous) @ asyncToGenerator.js:21 (anonymous) @ client-registration.js:53 (anonymous) @ index.js:8 dispatch @ VM12:3665 confirmClientRegistration @ CreateUserPage.js:149 (anonymous) @ CreateUserPage.js:109 l @ runtime.js:63 (anonymous) @ runtime.js:294 (anonymous) @ runtime.js:119 n @ asyncToGenerator.js:3 o @ asyncToGenerator.js:25 (anonymous) @ asyncToGenerator.js:32 (anonymous) @ asyncToGenerator.js:21 (anonymous) @ CreateUserPage.js:107 (anonymous) @ verification.slide.js:54 l @ runtime.js:63 (anonymous) @ runtime.js:294 (anonymous) @ runtime.js:119 n @ asyncToGenerator.js:3 o @ asyncToGenerator.js:25 (anonymous) @ asyncToGenerator.js:32 (anonymous) @ asyncToGenerator.js:21 (anonymous) @ verification.slide.js:54 (anonymous) @ MultipartDynamicForm.js:57 l @ runtime.js:63 (anonymous) @ runtime.js:294 (anonymous) @ runtime.js:119 n @ asyncToGenerator.js:3 o @ asyncToGenerator.js:25 (anonymous) @ asyncToGenerator.js:32 (anonymous) @ asyncToGenerator.js:21 (anonymous) @ MultipartDynamicForm.js:51 (anonymous) @ MultipartDynamicForm.js:38 onClick @ SplitLayoutDynamicForm.js:40 o @ react-dom.production.min.js:14 d @ react-dom.production.min.js:14 (anonymous) @ react-dom.production.min.js:14 A @ react-dom.production.min.js:15 it @ react-dom.production.min.js:52 rt @ react-dom.production.min.js:51 ot @ react-dom.production.min.js:52 dt @ react-dom.production.min.js:56 j @ react-dom.production.min.js:287 D @ react-dom.production.min.js:19 Jt @ react-dom.production.min.js:70 Xt @ react-dom.production.min.js:69 t.unstable_runWithPriority @ scheduler.production.min.js:19 Rr @ react-dom.production.min.js:122 B @ react-dom.production.min.js:287 Gt @ react-dom.production.min.js:68 r @ helpers.ts:99 r @ build.umd.js:961 Show 38 more frames build.umd.js:2977 [DEBUG] 52:15.421 RestClient - PATCH {endpoint: 'REDACTED, region: 'us-east-1', service: 'execute-api', custom_header: undefined} build.umd.js:2977 [DEBUG] 52:15.422 Credentials - getting credentials build.umd.js:2977 [DEBUG] 52:15.423 Credentials - picking up credentials build.umd.js:2977 [DEBUG] 52:15.423 Credentials - getting new cred promise build.umd.js:2977 [DEBUG] 52:15.423 Credentials - checking if credentials exists and not expired build.umd.js:2977 [DEBUG] 52:15.423 Credentials - need to get a new credential or refresh the existing one build.umd.js:2977 [DEBUG] 52:15.423 Credentials - no credentials for expiration check build.umd.js:2977 [DEBUG] 52:15.424 AuthClass - Getting current user credentials build.umd.js:2977 [DEBUG] 52:15.424 AuthClass - Getting current session build.umd.js:2977 [DEBUG] 52:15.424 AuthClass - Failed to get user from user pool build.umd.js:2977 [DEBUG] 52:15.424 AuthClass - Failed to get the current user No current user build.umd.js:2977 [DEBUG] 52:15.424 AuthClass - getting session failed No current user build.umd.js:2977 [DEBUG] 52:15.424 Credentials - setting credentials for guest build.umd.js:2977 [DEBUG] 52:15.425 RestClient - No credentials available, the request will be unsigned ```

aws-exports.js

No response

Manual configuration

import config from "./config";

export const amplifyConfiguration = {
  Auth: {
    mandatorySignIn: true,
    region: config.cognito.REGION,
    userPoolId: config.cognito.USER_POOL_ID,
    identityPoolId: config.cognito.IDENTITY_POOL_ID,
    userPoolWebClientId: config.cognito.APP_CLIENT_ID,
  },
  Storage: {
    region: config.s3.REGION,
    bucket: config.s3.BUCKET,
    identityPoolId: config.cognito.IDENTITY_POOL_ID
  },
  API: {
    endpoints: [
      {
        name: config.apiGateway.NAME,
        endpoint: config.apiGateway.URL,
        region: config.apiGateway.REGION
      },
    ]
  }
};

Additional configuration

No response

Mobile Device

No response

Mobile Operating System

No response

Mobile Browser

No response

Mobile Browser Version

No response

Additional information and screenshots

https://user-images.githubusercontent.com/57410369/192565921-2f64689c-fe1a-4a1f-bcb4-0a2f39ebb827.mp4

Video of sporadic issue, succeeds once, fails once. You can see the user created in the pool, and you can see the user immediately deleted from the pool with no additional interaction.

The register button fires one function, the one referenced above in the Code Snippet field.

This integration has been functioning with no issues for over 3 years.

hope-portal-services commented 2 years ago

Left this out, but after the signup succeeds, this is the result:

Screen Shot 2022-09-27 at 12 26 38 PM

The user is deleted from the pool before even having a chance to enter the verification code sent to the email.

nadetastic commented 2 years ago

Hi @hope-portal-services 👋

Thanks for opening this issue, I'd like to request some additional information before diving into it.

Since you mentioned that the user is being deleted after sign up, could you take a look and share the CloudTrail event logs for a DeleteUser Cognito API action related to one of the users facing this issue? This may help in determining what/why the user is being deleted. Also can we verify that the user is actually being created by locating a corresponding AdminCreateUser API action?

Please note to redact any sensitive info 🙂

hope-portal-services commented 2 years ago

Hi @nadetastic,

So these users are being created using Amplify, ie: from the Javascript Library using Auth.SignUp(), would those events show up in CloudTrail? If so, I'm not seeing any of them. I see Events from AdminCreateUser which is used elsewhere in the application when a user is created manually, but not through the registration process.

Heres a log from the SignUp Event:

{
    "eventVersion": "1.08",
    "userIdentity": {
        "type": "Unknown",
        "principalId": "Anonymous"
    },
    "eventTime": "2022-09-28T16:29:20Z",
    "eventSource": "cognito-idp.amazonaws.com",
    "eventName": "SignUp",
    "awsRegion": "us-east-1",
    "sourceIPAddress": "REDACTED",
    "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36",
    "requestParameters": {
        "clientId": "REDACTED",
        "username": "HIDDEN_DUE_TO_SECURITY_REASONS",
        "password": "HIDDEN_DUE_TO_SECURITY_REASONS",
        "userAttributes": "HIDDEN_DUE_TO_SECURITY_REASONS"
    },
    "responseElements": {
        "userConfirmed": false,
        "codeDeliveryDetails": {
            "destination": "HIDDEN_DUE_TO_SECURITY_REASONS",
            "deliveryMedium": "EMAIL",
            "attributeName": "email"
        },
        "userSub": "REDACTED"
    },
    "additionalEventData": {
        "sub": "REDACTED"
    },
    "requestID": "c4825548-5c67-49f2-8aac-c4b70c84469a",
    "eventID": "3353ed0b-19c7-4063-aacc-9c5c094e8325",
    "readOnly": false,
    "eventType": "AwsApiCall",
    "managementEvent": true,
    "recipientAccountId": "REDACTED",
    "eventCategory": "Management",
    "tlsDetails": {
        "tlsVersion": "TLSv1.2",
        "cipherSuite": "ECDHE-RSA-AES128-GCM-SHA256",
        "clientProvidedHostHeader": "cognito-idp.us-east-1.amazonaws.com"
    }
}

For DeleteUser there are 0 Events in CloudTrail, I think because that function is for account users, but I AM seeing a lot of AdminDeleteUser Events.

ie:

{
    "eventVersion": "1.08",
    "userIdentity": {
        "type": "IAMUser",
        "principalId": "REDACTED",
        "arn": "REDACTED",
        "accountId": "REDACTED",
        "accessKeyId": "REDACTED",
        "userName": "REDACTED"
    },
    "eventTime": "2022-09-28T16:29:21Z",
    "eventSource": "cognito-idp.amazonaws.com",
    "eventName": "AdminDeleteUser",
    "awsRegion": "us-east-1",
    "sourceIPAddress": "REDACTED",
    "userAgent": "aws-sdk-nodejs/2.1223.0 linux/v12.22.11 exec-env/AWS_Lambda_nodejs12.x promise",
    "requestParameters": {
        "userPoolId": "REDACTED",
        "username": "HIDDEN_DUE_TO_SECURITY_REASONS"
    },
    "responseElements": null,
    "additionalEventData": {
        "sub": "REDACTED"
    },
    "requestID": "1c5d2bc6-51b3-4287-84f4-b364a8338dc6",
    "eventID": "81fcb6ae-e664-4fa8-bee1-5f778dc8df29",
    "readOnly": false,
    "eventType": "AwsApiCall",
    "managementEvent": true,
    "recipientAccountId": "REDACTED",
    "eventCategory": "Management",
    "tlsDetails": {
        "tlsVersion": "TLSv1.2",
        "cipherSuite": "ECDHE-RSA-AES128-GCM-SHA256",
        "clientProvidedHostHeader": "cognito-idp.us-east-1.amazonaws.com"
    }
}

In the video attached, toward the end you can see that the user does get created, it shows up in the user pool and then disappears on the next reload of the user pool, ie: within about 5 seconds.

This is the obvious result of trying to confirm the user with their verification code once the user has been deleted:

{
    "eventVersion": "1.08",
    "userIdentity": {
        "type": "Unknown",
        "principalId": "Anonymous"
    },
    "eventTime": "2022-09-28T16:29:31Z",
    "eventSource": "cognito-idp.amazonaws.com",
    "eventName": "ConfirmSignUp",
    "awsRegion": "us-east-1",
    "sourceIPAddress": "REDACTED",
    "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36",
    "errorCode": "UserNotFoundException",
    "errorMessage": "Username/client id combination not found.",
    "requestParameters": {
        "clientId": "REDACTED",
        "username": "HIDDEN_DUE_TO_SECURITY_REASONS",
        "confirmationCode": "HIDDEN_DUE_TO_SECURITY_REASONS",
        "forceAliasCreation": true
    },
    "responseElements": null,
    "requestID": "572c01e3-4f86-4293-a71d-6badb42ee51e",
    "eventID": "e89c6008-50f1-4bc4-bd07-b8e7d05239fe",
    "readOnly": false,
    "eventType": "AwsApiCall",
    "managementEvent": true,
    "recipientAccountId": "REDACTED",
    "eventCategory": "Management",
    "tlsDetails": {
        "tlsVersion": "TLSv1.2",
        "cipherSuite": "ECDHE-RSA-AES128-GCM-SHA256",
        "clientProvidedHostHeader": "cognito-idp.us-east-1.amazonaws.com"
    }
}
hope-portal-services commented 2 years ago

@nadetastic I want to note here that while I cannot reliably duplicate this issue since its so sporadic, I'd like to note that most often the issue is seen when navigating to the page FROM a Gmail email, ie: Clicking a link that says for example: Register which navigates to my registration page.

I know Gmail manipulates its body links to be for example, https://google.com/url?q={YOUR-ORIGINAL-URL}?{GOOGLES-CUSTOM-TRACKING-PARAMETERS}

I'm wondering if any of these parameters could be conflicting with Amplify and causing some anomaly.

I did a side by side comparison of the URLs for reference: Left is Google's manipulated URL, Right is the original.

Screen Shot 2022-09-28 at 5 05 09 PM
hope-portal-services commented 2 years ago

@nadetastic - Updating here:

Have been heavily testing this using Proton Mail in place of Gmail to try to rule out something within my own code, cannot duplicate this bug so far, 30+ attempts, seems specific to Gmail redirected URLs.

Edit: Switched back to Gmail, bug present on attempt 1.

hope-portal-services commented 2 years ago

@nadetastic Still cannot pinpoint this to my code, this poses a pretty huge issue considering our invite system runs on email which contains URL parameters. As long as those redirects are coming from Gmail, this issue seems to be present.

Not sure where to turn next aside from revamping the entire system to work differently, even then, this is a really inconvenient issue right now and testing every permutation of this bug is massively time consuming.

Any chance of a followup from engineering?

nadetastic commented 2 years ago

@hope-portal-services thank you for the update.

Regarding the CloudTrail logs, since we are seeing AdminDeleteUser API calls, can you confirm from your end which entity is making this call? This will be under userIdentity. This can help us trace where the trigger to remove the user is originating from

hope-portal-services commented 2 years ago

Hi @nadetastic - The identity making the call is indeed one of my application users, meant to run admin-level functions like this, but this identity is used throughout the application for AWS related functions, so it would seem normal that the request would come from the identity configured in the application. Question is, what is causing this particular function to run, it's allowed to run because it has permission to.

nadetastic commented 2 years ago

@hope-portal-services This looks like a pretty unique issue that I am not yet able to reproduce. Would you be able to provide me with a sample repo of that I can use to test with?

One thing Im curious about, is if we can confirm that your method for deleting users is indeed getting invoked. There are two ways to do this:

nadetastic commented 2 years ago

An additional note, by default, adding Admin actions doesn't setup a route for deleteUser, I am assuming that you manually added that to yourself.

hope-portal-services commented 2 years ago

Hi @nadetastic -

Correct, I use Amplify, but not the automated setup, a manual setup in this case.

I've made every change I can think of to halt this additional call to the API and I can NEVER see this route being called twice from the frontend, even went as far as to make sure dev tools stays open the entire time to see if any initial calls are sent from the frontend. Nothing.

When this anomaly occurs, if you watch the network, it only ever gets fired off one time, so this really doesn't feel like an issue coming from the frontend.

Here's a log of the bug when it happens: The functions runs so incredibly close together, it almost seems like a retry, but this function is not asynchronous, so I don't believe Lambda max retries even apply here.

For some additional insight: This function checks for a registration in progress, for example: A user begins registration and gets past the point where a cognito user is created, but has not finished registration far enough to create a user record in my database.

This function checks for that scenario, if the function finds a cognito user with the matching email, but does not find a user record, we delete the cognito user and return a Great, this email is available rather than This email is in use, etc...

This had been working as expected for years. I truly don't know what has changed here, but it feels like it's either:

  1. Amplify firing off the request twice, one of them being hidden because only 1 can ever be observed from the Network (I use the API provided by amplify to make the requests with a token, etc..)
  2. Lambda running more than once

At this point, I really am at a loss.

I suppose a way to duplicate the actions that I'm taking would be to run two adminGetUser functions back to back, in which case one of them is returning a user and one is not.

The obvious scenario that is happening here is:

  1. User begins registration, I check the email for validity
  2. Email valid, user moves on
  3. User completes registration, Cognito user is created, verification code is sent to email
  4. Email check runs a second time, deletes the user
  5. User enters verification code, submits it, registration fails, user can not be found
Screen Shot 2022-10-06 at 10 38 21 AM

Video of the actual process: https://user-images.githubusercontent.com/57410369/194350873-350a3718-3de0-474f-8748-be86273ecba5.mov

-- Email check Lambda

Screen Shot 2022-10-06 at 12 06 05 PM

Cognito user lookup

Screen Shot 2022-10-06 at 12 06 18 PM

Delete cognito user

Screen Shot 2022-10-06 at 12 06 27 PM
hope-portal-services commented 2 years ago

@nadetastic - Happy to say this has been solved. Thank you for your help either way, it was helpful to hash this out here.

For reference, Google's redirects use a headless Chromium to render your URL's to check for blacklisted or malicious sites. In doing this, it means that anything that happens to run on your URL, will run. In this case, the site passed Google's checks quickly, redirected our URL and likely sits in a queue before being checked which is why it takes about 20-30 seconds to clear, in which case, we have already reached the end of registration, created our Cognito user and boom, the site renders on Google's servers, runs the email check for validity and the Cognito user is deleted.

I was able to block Google's IP's from hitting our email check Lambda and problem solved.

Feel free to close out this issue, thanks.

nadetastic commented 2 years ago

@hope-portal-services glad to hear that this was resolved. Thanks for the context on the root cause as it help me in understanding the problem and I can share with the team.

Will close this out.