FusionAuth / fusionauth-issues

FusionAuth issue submission project
https://fusionauth.io
90 stars 12 forks source link

Disable email verification on user signup (via create user and registration combined API) #1732

Open Rao-Muneeb opened 2 years ago

Rao-Muneeb commented 2 years ago

Disable email verification on user signup (via create user and registration combined API)

Description

Hello,

Hope you all are doing well. I was looking for functionality in which a user gets signed up using create user and registration (combined) API but verification email should not trigger. I have already come across with skipVerification field but setting this field also marks the email as verified. What I want is the user gets signed up, the email should not trigger at that time and later user would verify his/her email via some other flow using FusionAuth resend verification email and verify user email APIs.

I have already asked that question on FusionAuth Forum and was redirected to file a query here as the solution provided there was not solving the issue.

Affects versions

All versions.

Related

mooreds commented 2 years ago

Hi @Rao-Muneeb

Thanks for filing an issue. I'm sorry for any confusion, but I asked for a step by step set of instructions that would help us determine if there was a bug (doc or code).

Please provide an excruciatingly detailed list of steps you took as well as what you are trying to achieve. That will help us determine if this is an issue.

No detail is too small :) and screen shots are great.

Thanks!

Rao-Muneeb commented 2 years ago

Thanks @mooreds for your response... Below code is written in NestJs... FuisonAuth version: 1.32.1...

Steps to Reproduce the issue

1- FusionAuth Tenant's Email settings

Screen Shot 2022-05-27 at 11 59 37 AM

2- Integrate FusionAuth with SendGrid for sending emails.

3- Signup User method

async signUpUser(res: Response, signUpDto: SignUpDTO): Promise<UUID> {
    try {
      // create user and registration (combined) API (link mentioned in the description above)
      const userRegistration = await this.fusionClient.register(undefined, {
        registration: {
          applicationId: fusionAuthService.getClientId(),
        },
        user: {
          firstName: signUpDto.firstName,
          lastName: signUpDto.lastName,
          username: signUpDto.userName,
          email: signUpDto.email,
          password: signUpDto.password,
        },
        // this is the flag that stops email issuance but also marked the email as verified. What I want, neither
        // email should be emitted nor email should be marked as verified on user signup.
        skipVerification: true,
      });

      this.logger.log(
        `User=${signUpDto.email} registered in fusionauth successfully.`,
      );

      // add user in the table
      await this.userService.addUser(
        UserMapper.fromDTOtoEntity(userRegistration.response.user),
      );

      this.logger.log(
        `User=${signUpDto.email} added in the table successfully.`,
      );

      res.cookie(
        cookieConstants.ACCESS_TOKEN,
        userRegistration.response.token,
        accessTokenOptions,
      );

      res.cookie(
        cookieConstants.REFRESH_TOKEN,
        userRegistration.response.refreshToken,
        refreshTokenOptions,
      );

      return userRegistration.response.user.id;
    } catch (error) {
      this.logger.error(
        `Error occurred during user signing-up process. Error=${JSON.stringify(
          error,
        )}`,
      );

      // this error emits from FusionAuth.
      if (error.statusCode === 400) {
        throw new ConflictException("User already existed.");
      }

      // this error is generated by phone validator.
      if (error.status === 400) {
        throw error;
      }

      throw new InternalServerErrorException("Something went wrong!");
    }
  }

Hope this would help in determining the issue...

mooreds commented 2 years ago

Thanks for the details, @Rao-Muneeb ! That is very helpful!

To reiterate the issue, it looks to me like the problem is if you:

FusionAuth should:

But it is marking the account email as verified. Is that correct?

A workaround might be to create the user and the registration separately, and skip verification on both, which should not implicitly mark the user's email as verified. Have you tried that?

Finally, just to double check, have you confirmed this behavior with the latest version of FusionAuth?

Rao-Muneeb commented 2 years ago

@mooreds Yes, that is the behavior I want...

I just tested the workaround you mentioned but still the same behavior i.e. FusionAuth still marking the email account as verified if skipVerification and skipRegistrationVerification flags are set to true. Below is the code,

async signUpUser(res: Response, signUpDto: SignUpDTO): Promise<UUID> {
    try {
      // const userRegistration = await this.fusionClient.register(undefined, {
      //   registration: {
      //     applicationId: fusionAuthService.getClientId(),
      //   },
      //   user: {
      //     firstName: signUpDto.firstName,
      //     lastName: signUpDto.lastName,
      //     username: signUpDto.userName,
      //     email: signUpDto.email,
      //     password: signUpDto.password,
      //   },
      //   skipVerification: true,
      // });

      // first create a user with skipVerification flag set to true
      const userResponse = await this.fusionClient.createUser(undefined, {
        applicationId: fusionAuthService.getClientId(),
        skipVerification: true,
        user: {
          firstName: signUpDto.firstName,
          lastName: signUpDto.lastName,
          username: signUpDto.userName,
          email: signUpDto.email,
          password: signUpDto.password,
        },
      });

      //then register the user with skipRegistrationVerification flag set to true
      const userRegistration = await this.fusionClient.register(
        userResponse.response.user.id,
        {
          skipRegistrationVerification: true,
          registration: {
            applicationId: fusionAuthService.getClientId(),
          },
        },
      );

      this.logger.log(
        `User=${signUpDto.email} registered in fusionauth successfully.`,
      );

      // add user in the table
      await this.userService.addUser(
        UserMapper.fromDTOtoEntity(userResponse.response.user),
      );

      this.logger.log(
        `User=${signUpDto.email} added in the table successfully.`,
      );

      res.cookie(
        cookieConstants.ACCESS_TOKEN,
        userRegistration.response.token,
        accessTokenOptions,
      );

      res.cookie(
        cookieConstants.REFRESH_TOKEN,
        userRegistration.response.refreshToken,
        refreshTokenOptions,
      );

      return userResponse.response.user.id;
    } catch (error) {
      this.logger.error(
        `Error occurred during user signing-up process. Error=${JSON.stringify(
          error,
        )}`,
      );

      // this error emits from FusionAuth.
      if (error.statusCode === 400) {
        throw new ConflictException("User already existed.");
      }

      // this error is generated by phone validator.
      if (error.status === 400) {
        throw error;
      }

      throw new InternalServerErrorException("Something went wrong!");
    }
  }

I also tested both cases (with combined and separate APIs) with the latest version of FusionAuth 1.36.4 but resulted in the same functionality...

Screen Shot 2022-05-28 at 11 40 39 AM
Rao-Muneeb commented 2 years ago

@mooreds any suggestions regarding this?

glen-84 commented 2 years ago

I also noticed recently that when you disable Verify email (in the admin UI), a user's email address is set to verified by default. I don't think that this is the correct behaviour, it should probably default to false.

robotdan commented 2 years ago

This is all working as designed.

If you set skipVerification: true as @Rao-Muneeb is doing - you are explicitly telling FusionAuth to skip the email verification process and mark the user as verified. So that is doing exactly what I expect it to do.

If you disable email verification at the tenant level, the intended design is to mark the email as verified because there is no process to ever verify the user's email - so it is implicitly verified. You could make the argument that I think @glen-84 is making -that we should leave it as un-verified, and then allow you to re-enable at a later point in time. That is a reasonable idea and could be a feature request.

As far as leaving Email Verification enabled, but not initiating this workflow during the user create as @Rao-Muneeb is asking - I think what you are looking for is another API option to say "do not send the user an Email Verification email". This would be a feature request. If this is what you're looking for, can you describe the use case for this behavior?

glen-84 commented 2 years ago

You could make the argument that I think @glen-84 is making -that we should leave it as un-verified, and then allow you to re-enable at a later point in time.

Well, I was initially looking at the idea of disabling email verification in order to bypass the issue in #1738, and instead send the (localized) email via the API, but apart from some other challenges, disabling email verification in FA would mean that you miss out on features like "implicit verification" and "verify email when changed", so it's not ideal.

My point above was simply that if email verification is disabled in FA, a user's email address should not be considered "verified", since they never actually verified it. It should remain unverified (which yes, also makes sense if one were to re-enable verification).

I was under the impression that you could get verification tokens and patch a user's verification status via the API, but I may have been wrong about that.

Rao-Muneeb commented 2 years ago

@robotdan yes, that would be great If an API gets some sort of an option that tells FusionAuth not to send a verification email during user creation.

Use Case

Actually, I am developing an extension + web app whose behavior is that let the user use the extension and web app without verifying his/her email right after the sign-up for a seamless behavior. But in order to exploit the extension fully, there are cases where the user needs to verify his/her email and phone number via OTP later. Therefore, I don't want to send an extra and meaningless verification email to the user as there is no such flow to verify the email right after the registration in my case.

Please let me know if this helps and if it is taken as a feature request then also let me know where I can see that feature status and ETA.

Thanks.

robotdan commented 2 years ago

Summary:

  1. Add an option to set the default user.verified state when Email Verification is disabled. Current behavior is to mark verified, but this should be a config option at the tenant. Maybe the same for Registration Verification?
  2. Add another option to the User API to be able to create a user with user.verified: false but do not send the user an email.

@Rao-Muneeb could you use Registration Verification for your use case instead? It is essentially just the same as Email Verification but at the registration level instead of the user. So you could allow the user to verify their email address, or not, and then ask the user to verify their registration at a later point in time to unlock more features. If you do this via the APIs, you can communicate it to the user however you wish. Although - this may have the same problem Email Verification has in that I think we'll send an email right away if you choose to use Registration Verification.

Rao-Muneeb commented 2 years ago

@robotdan yes, the summary is perfect. For now, we are planning for a beta release of the product soon and decided to go with the same email verification behavior and keeping in mind that that issue would get resolved soon. So that we can rectify this email verification behavior later. :)

I would be waiting for that feature. Could you please tell me from where I can track the issue status so that I would know the possible expected ETA of that feature?

Thanks.

robotdan commented 2 years ago

Thanks @Rao-Muneeb . All issue tracking is done in GH - so you can just track this issue status.

Diane-Rose22 commented 2 months ago

This is an older request, but I would very much like to see it implemented for our use case.

For our workflow - we do not use self-service registration and instead users are configured manually. Because of this, after a user is created sometimes more work is needed before we want to invite users into our system.

I want to be able to create users with an 'unverified' state, and manually send the email to 'verify' their email the first time they login. I still want to use the client 'verified' flag because I do want it to be set on password changes, and undone when an email is modified. In my situation I would prefer your suggested fix (2) above

Add another option to the User API to be able to create a user with user.verified: false but do not send the user an email.

This is already possible from the /users/import api but not from the /users/create route.

mt-andreas commented 1 week ago

Hi, we would also love this option. The ability to disable automatic emails being sent on registration and having the default state to unverified.

Thanks!