amplify-js icon indicating copy to clipboard operation
amplify-js copied to clipboard

How to verify if a user with a given email already exists in User Pool?

Open wzup opened this issue 6 years ago • 46 comments

Do you want to request a feature or report a bug?

Question

What is the expected behavior?

Check is user exists

example

Is there such a method?

Auth.verifyIfUserExists({
    email: '[email protected]'
})
.then(res => {
    // user with this email already exists
})

Because now I can find out if a user exists ONLY during signUp action. But I want to check it before I do signUp. Because if a user doesn't exist it will be created right off the bat. And it is not what expected

Auth.signUp({
        username,
        password,
        attributes: {
        },
    })
    .then(data => console.log(data))
    .catch(err =>{
        // User exists !!
    });

wzup avatar Jun 20 '18 17:06 wzup

@wzup

There is not function that does this and only this; however, I think if you use the confirmSignUp function and are using email as an alias you will get back an AliasExistsException error.

In any case I am marking this as a feature request, as it seems useful.

Thanks for your feedback.

haverchuck avatar Jun 20 '18 18:06 haverchuck

I did:

 userExist(userName: string) {
      return Auth.signIn(userName, '123');
    }
and

userExist(email: string) {
    return this.cognitoService.userExist(email.toLowerCase()).then(res => {
        return false;
    }).catch(error => {
        const code = error.code;
        console.log(error);
        switch (code) {
            case 'UserNotFoundException':
                return !this.redirectToRegister(email);
            case 'NotAuthorizedException':
                return true;
            case 'PasswordResetRequiredException':
              return !this.forgotPassword(email);
            case 'UserNotConfirmedException':
                return !this.redirectToCompleteRegister(email);
            default:
                return false;
        }
    });
    }

michelmob avatar Jun 21 '18 03:06 michelmob

// one
userExist( userName: string ) {
    return Auth.signIn( userName, '123' );
}

// two
userExist( email: string ) {
    return this.cognitoService.userExist( email.toLowerCase() )
        .then( res => {
            return false;
        } )
        .catch( error => {
            const code = error.code;
            console.log( error );
            switch ( code ) {
                case 'UserNotFoundException':
                    return !this.redirectToRegister( email );
                case 'NotAuthorizedException':
                    return true;
                case 'PasswordResetRequiredException':
                    return !this.forgotPassword( email );
                case 'UserNotConfirmedException':
                    return !this.redirectToCompleteRegister( email );
                default:
                    return false;
            }
        } );
}

wzup avatar Jun 21 '18 06:06 wzup

@michelmob , thank you.

One question though. What is this in your example? Where does .cognito live?

return this.cognitoService.userExist( email.toLowerCase() )

wzup avatar Jun 21 '18 06:06 wzup

This is an abstraction of cognito using aws amplify.

Sent from my iPhone

On 21 Jun 2018, at 03:13, wzup [email protected] wrote:

@michelmob , thank you.

One question though. What is this in your example? Where does .cognito live?

return this.cognitoService.userExist( email.toLowerCase() ) — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or mute the thread.

michelmob avatar Jun 21 '18 11:06 michelmob

@michelmob @haverchuck

But what if credentials are correct?

Auth.signIn(userName, '123');

Then a user that wants to sign up will be suddenly signed in instead.

This is definitely bad experience from aws-amplify.

wzup avatar Jun 26 '18 10:06 wzup

@haverchuck

Here is why requested method I ask for is important.

With current authflow we have to signin and then signout a user just to check if an email (username) already exists:

// 1. In order to check if an email already exists in Cognito we have to call .signIn.
// Because there is no special method for that, like Auth.doesUsernameExists(username)
Auth.signIn( email, password )
    .then( user => {
        // 2. I a user found, they get signin
        // You have to log out a user if found
        // Security vulnerability
        return Auth.signOut();
    } )
    .then( res => {
        // 3. Here we show a user that email is taken
        // After logging them in and logging them out. LOL
        this.setState((state, props) => {
            return {
                emailError: 'This email is already taken'
            };
        });
        return;     
    } )
    .catch( err => {
        switch ( err.code ) {
            case 'UserNotFoundException':
                // Only here, in .catch error block we actually send a user to sign up
                return this.signUp();
            case 'NotAuthorizedException':
                return true;
            case 'PasswordResetRequiredException':
                return false;
            case 'UserNotConfirmedException':
                return this.props.navigation.navigate('ConfirmRegistrationScreen', {
                    username: email,
                });
            default:
                return false;
        }
    } )

wzup avatar Jun 27 '18 07:06 wzup

@wzup The sign-in workaround might work just because User Pools require passwords that are 6-characters are longer so in practice, there will never be a user account whose password is '123'.

@haverchuck However, even if this workaround works, it's really bad that the API doesn't support checking the existence of a user name directly. I've been working with Cognito for two years and this feature already exists as a request, but hasn't been implemented yet, along with the ability for an administrator to reset an account's password. The combination of these two problems makes it quite difficult to build enterprise applications.

tcchau avatar Jul 05 '18 14:07 tcchau

If you can change your User Pool, you can achieve email uniqueness following the step at Forcing Email Uniqueness in Cognito User Pools section in Authentication

It is just below the Sing Up section.

nabarunchakma avatar Jul 20 '18 00:07 nabarunchakma

Are there any updates on this issue?

cor avatar Sep 09 '18 10:09 cor

@wzup

There is not function that does this and only this; however, I think if you use the confirmSignUp function and are using email as an alias you will get back an AliasExistsException error.

In any case I am marking this as a feature request, as it seems useful.

Thanks for your feedback.

I looked at the amplify source code.

Auth.confirmSignup() calls cognitoUser.confirmRegistration(code, forceAliasCreation) which then calls this API: https://docs.aws.amazon.com/cognito-user-identity-pools/latest/APIReference/API_ConfirmSignUp.html

The right way to do this (without signIn & signOut) according to the project contributors seems to be this:

const code = '000000'
Auth.confirmSignUp(username, code, {
    // If set to False, the API will throw an AliasExistsException error if the phone number/email used already exists as an alias with a different user
    forceAliasCreation: false
}).then(data => console.log(data))
  .catch( err => {
        switch ( err.code ) {
            case 'UserNotFoundException':
                return true;
            case 'NotAuthorizedException':
                return false;
            case 'AliasExistsException':
                // Email alias already exists
                return false;
            case 'CodeMismatchException':
                return false;
            case 'ExpiredCodeException':
                return false;
            default:
                return false;
        }
    } )

heri16 avatar Nov 07 '18 03:11 heri16

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Jun 15 '19 22:06 stale[bot]

It's been a long time since this thread is closed, but I could find a right solution in Cognito SDK. You may use listUsers function to get user details: let params = { UserPoolId: "eu-central-1_xxxxxxx", Filter: "email="[email protected]"" }; cognito.listUsers(params,function(err,data){ // data object contains all the attributes of the user }

Note: Filter can be of any other attributes in user pool and "=" can be replaced by few other operators. Please refer "Cognitoidentityserviceprovider SDK

Cognitoidentityserviceprovider SDK " for more details

saikishored avatar Nov 01 '19 10:11 saikishored

With the above solution when you try to add the credentials to the CISP it doesn't work

logi-dc avatar Dec 19 '19 18:12 logi-dc

Amplify's default way to handle this scenario is at signUp call. If user exists it will throw UsernameExistsException exception. I have tested it with Usernames .

prog585 avatar Dec 19 '19 18:12 prog585

I want to check if a userExists in the pool at a different time to signup is there a method that allows that

logi-dc avatar Dec 19 '19 18:12 logi-dc

The default way I mentioned above (as per my research which was a thorough exercise), if otherwise you want to check before final signUp then that can be achieved by using some admin functions (please see the list of admin functions on cognito sdk docs). In calling admin functions you need to think about security perspectives though.

prog585 avatar Dec 19 '19 19:12 prog585

okay thank you

logi-dc avatar Dec 19 '19 19:12 logi-dc

Hi, i there any update on this issue?

akeditzz avatar Sep 23 '20 09:09 akeditzz

@akeditzz confirm signup test works perfectly as mentioned previously in this thread. just pass an obvious wrong otp and it will give error. one error is user not exist or something like that. if you got that error then email doesn't exist in pool. this only works if you use email as an alias for logging in and was verified though

anees17861 avatar Sep 23 '20 11:09 anees17861

@anees17861 dose it not work for mobile number ?

akeditzz avatar Sep 23 '20 11:09 akeditzz

@akeditzz it'll work, i use mobile number personally but have tested both. but same rules apply as for email. if signed up it needs to be verified or it won't work properly. Personally in my case if something goes wrong and user wasn't able to confirm, i just make him signup again with another username (random uuid in my case). So there will be two accounts created in cognito pool but only one will be confirmed and thus used for future login.

anees17861 avatar Sep 23 '20 11:09 anees17861

@akeditzz if you are using mobile number remember to use e164 format only. Country specific phone strings will give you issues

anees17861 avatar Sep 23 '20 11:09 anees17861

Thank you @anees17861 i will try.

akeditzz avatar Sep 23 '20 11:09 akeditzz

When I try to use solution proposed by @heri16. I always get error code ExpiredCodeException with message Invalid code provided, please request a code again.

PavolHlavaty avatar Oct 02 '20 07:10 PavolHlavaty

@PavolHlavaty are you sending the code as empty string or a non numerical value? Cognito may be doing format check before proceeding. I send only '00' and it works perfectly.

anees17861 avatar Oct 03 '20 04:10 anees17861

@anees17861 I am sending same string as @heri16 in his answer.

PavolHlavaty avatar Oct 03 '20 08:10 PavolHlavaty

@PavolHlavaty have you checked for both signed up and non signed up user? And what is the type of username you've selected. When I had set up, i was provided with 2 options. one is a username with email and phone as alias and other is using no separate username but rather directly phone number/email as username. I had chosen the first one. Maybe that makes a difference

anees17861 avatar Oct 03 '20 08:10 anees17861

I always get error code ExpiredCodeException with message Invalid code provided, please request a code again.

@PavolHlavaty I was experiencing the same issue as you until reading these docs: https://docs.aws.amazon.com/cognito/latest/developerguide/cognito-user-pool-managing-errors.html

Basically the PreventUserExistenceErrors option has to be disabled (ie. enable user existence errors) for your app client, where it was enabled by default in my config.

It can be changed in Cognito console with: User pools > Selecting your user pool > General settings > App clients > Show Details > Security configuration > Legacy > Save app client changes

With my default setup of amplify I have two app clients (native and web), and ended up changing it for both of them, although don't know if it that was necessary.

lfur avatar Jan 25 '21 06:01 lfur

Came across this issue and @heri16's solution worked perfect. If you're looking for something to just paste in and go, here's a snippet:

const usernameAvailable = async (username) => {
  // adapted from @herri16's solution: https://github.com/aws-amplify/amplify-js/issues/1067#issuecomment-436492775
  try {
    const res = await Auth.confirmSignUp(username, '000000', {
      // If set to False, the API will throw an AliasExistsException error if the phone number/email used already exists as an alias with a different user
      forceAliasCreation: false
    });
    // this should always throw an error of some kind, but if for some reason this succeeds then the user probably exists.
    return false;
  } catch (err) {
    switch ( err.code ) {
      case 'UserNotFoundException':
          return true;
      case 'NotAuthorizedException':
          return false;
      case 'AliasExistsException':
          // Email alias already exists
          return false;
      case 'CodeMismatchException':
          return false;
      case 'ExpiredCodeException':
          return false;
      default:
          return false;
    }
  }
}

To use:

const available = await usernameAvailable(emailAddress);
console.log(`user ${available ? 'available' : 'not available'}`);

fkunecke avatar Jun 04 '21 19:06 fkunecke