Skip to content

Ability to expire email verify token #2216

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 19, 2016
Merged

Ability to expire email verify token #2216

merged 1 commit into from
Jul 19, 2016

Conversation

cherukumilli
Copy link
Contributor

@cherukumilli cherukumilli commented Jul 6, 2016

Fixes #2198

Notes for this PR:

  • Provides an additional setting / config variables
    • emailVerifyTokenValidityDuration (not set by default)
if emailVerifyTokenValidityDuration is set to a number greater than 0 and verifyUserEmails is set to true then
  • Saves _email_verify_token_expires_at in the _User class along with the _email_verify_token at SignUp time
    • sets _email_verify_token_expires_at = currentTime + emailVerifyTokenValidityDuration
  • If the user clicks on the email verification link BEFORE _email_verify_token_expires_at then
    • update emailVerified to true in the User class (this code is already in place)
    • delete the _email_verify_token and _email_verify_token_expires_at fields for this user (new code)
    • respond back with a success (this code is already in place)
  • If the user completed email verification successfully but clicks on the link for second time then return an error message (i.e., fail gracefully)
  • If the user clicks on the email verification link AFTER the _email_verify_token_expires_at then
    • respond back with an error: email verification token expired.
    • user can try again and create a new account
      • deletion of rows in the _User class with expired verify email tokens is out of scope for this PR. Clean up should be handled by a job outside the normal auth flow.

Documentation updates

Updated README.md with the following:

   // if `verifyUserEmails` is `true` and
   //     if `emailVerifyTokenValidityDuration` is `undefined` then
   //        email verify token never expires
   //     else
   //        email verify token expires after `emailVerifyTokenValidityDuration`
   //
   // `emailVerifyTokenValidityDuration` defaults to `undefined`
   //
   // email verify token below expires in 2 hours (= 2 * 60 * 60 == 7200 seconds)
   emailVerifyTokenValidityDuration = 2 * 60 * 60, // in seconds (2 hours = 7200 seconds)

Testing

Added 10 new test cases in a new spec file EmailVerificationToken.spec.js

@cherukumilli
Copy link
Contributor Author

@flovilmart
Is there anyway you can run the tests once more?
The tests ran fine on my local pc one of them seems to be failing on travis

@flovilmart
Copy link
Contributor

Just restarted it :)

@cherukumilli
Copy link
Contributor Author

@flovilmart
Did you get a chance to review this pull request?

@flovilmart
Copy link
Contributor

flovilmart commented Jul 7, 2016 via email

@drew-gross
Copy link
Contributor

I think we should only add 1 new setting, the emailVerifyTokenValidityDuration. If the user leaves that as unset, then we can assume they don't want the token to expire.

username: username,
_email_verify_token: token
}, {emailVerified: true}).then(document => {
if (!document) {
}, {limit: 1}).then(results => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of this, I'd suggest adding a condition to the update that the current time must be before the expiry date, then if that fails you can do a query for the object to find the reason why it failed. That way the happy path requires one less query, and it will be less racy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good. I will incorporate your suggestion. I will add _email_verify_expires_at checking in the query for the update statement.

btw: You may know this already...if there is a failure then we redirect the user to the invalid_link. We currently don't send the error message/failure reason back with the invalid_link. If there is a request to send the failure reason then I will take a shot at it in another PR.

@cherukumilli
Copy link
Contributor Author

@flovilmart
In our earlier discussions, you indicated that it will be good if we have an additional setting like allowEmailVerifyTokenToExpire.

Are you ok if I change the code to use only one setting emailVerifyTokenValidityDuration as per the feedback from @drew-gross ?

If you are ok then I will:

  1. remove following line from ParseServer.js to unset the value by default
    emailVerifyTokenValidityDuration = 31536000, // 1 Year in seconds
  2. remove all references to allowEmailVerifyTokenToExpire
  3. Refactor all the code
  4. update test cases and documentation and
  5. submit another pull request

Please let me know either way :)

@flovilmart
Copy link
Contributor

IM all good with that :)

@ghost
Copy link

ghost commented Jul 9, 2016

@cherukumilli updated the pull request.

3 similar comments
@ghost
Copy link

ghost commented Jul 9, 2016

@cherukumilli updated the pull request.

@ghost
Copy link

ghost commented Jul 9, 2016

@cherukumilli updated the pull request.

@ghost
Copy link

ghost commented Jul 9, 2016

@cherukumilli updated the pull request.

@@ -151,6 +151,11 @@ export default {
help: "Prevent user from login if email is not verified and PARSE_SERVER_VERIFY_USER_EMAILS is true, defaults to false",
action: booleanParser
},
"emailVerifyTokenValidityDuration": {
env: "PARSE_EMAIL_VERIFY_TOKEN_VALIDITY_DURATION",
help: "Email verification token validity duration, defaults to 1 year",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doc needs update

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated doc

@ghost
Copy link

ghost commented Jul 10, 2016

@cherukumilli updated the pull request.

@drew-gross
Copy link
Contributor

Cool. I think is solid, but because it involves a new public API I'd like to give it a few more days for comments. We maya want to eg. discuss what happens to existing tokens when people enable this for the first time. Another alternative would be to store the time the token was created rather than the time it expires, then if someone shortens the validity duration, existing tokens will be valid for the length of the new duration, not the old one. Session expiry doesn't work that way, though, so it might be confusing.

@cherukumilli
Copy link
Contributor Author

@drew-gross
Your feedback got me thinking on other ways to expire email verify tokens without adding a new Public API.

I wonder if the following is a better solution than this PR:

  1. Developer maintains an internal setting called: emailVerifyTokenExpireDuration (either using a Config variable using Parse Dashboard or a constant in cloud code or a DB table entry, etc...)
  2. Developer creates a job / cloud code to run every 5 minutes
  3. Cloud code queries the Parse.User class to get all the Users with expired tokens using a query like:
    • emailVerified set to false
    • createdAt is less than currentTime - emailVerifyTokenExpireDuration
  4. Clean up all the Users returned in step 3

Please let me know if you see any gaps in the logic above.

@drew-gross
Copy link
Contributor

Hmm, I missed the context about deleting users who have not verified their email. I don't think thats a great idea, I think a better plan would be to resend the verification email (which can be done today by setting the email on the user. It will resend the email even if it's the same address). Then people could recover their account if they delete the original verification email.

That plan for doing user cleanup without a new public API seems solid, but adding expiry as a new API seems like it would be useful as well.

@cherukumilli
Copy link
Contributor Author

I didn't know that setting the email on the user will trigger another verification email.

Based on the feedback, I will add the following new test cases:

  1. clicking on the email verify link by an email VERIFIED user that was setup before enabling the expire email verify token should show an invalid link
  2. clicking on the email verify link by an email UNVERIFIED user that was setup before enabling the expire email verify token should show an invalid link
  3. setting the email on the user should set a new email verification token and new expiration date for the token when expire email verify token flag is set

@ghost
Copy link

ghost commented Jul 10, 2016

@cherukumilli updated the pull request.

1 similar comment
@ghost
Copy link

ghost commented Jul 10, 2016

@cherukumilli updated the pull request.

@flovilmart flovilmart modified the milestone: 2.2.17 Jul 12, 2016
@drew-gross
Copy link
Contributor

Code looks super solid and very well tested. One more api decision which we should consider is whether to let the client see this field, and/or edit it via regular Parse Queries, or strip it and not allow the client to see it.

If we decided we do want to allow the client to see/edit this field, we can merge as is.

@flovilmart
Copy link
Contributor

IMHO we should not (same as emailVerified)

@cherukumilli
Copy link
Contributor Author

@drew-gross
When you say whether to let the client see this field, is the field that you are talking about _email_verify_token_expires_at ?

If it is then it is hidden. I made it to mimic the _email_verify_token field to a large extent.

@drew-gross
Copy link
Contributor

I noticed in the tests you are using user.get('_email_verify_token_expires_at') though?

@cherukumilli
Copy link
Contributor Author

Let me fix that and push another update with a test case to ensure that the client cannot see the _email_verify_token_expires_at field.

@facebook-github-bot
Copy link

@cherukumilli updated the pull request.

@ghost
Copy link

ghost commented Jul 17, 2016

@cherukumilli updated the pull request.

@cherukumilli
Copy link
Contributor Author

@flovilmart
can you please restart the build?

@flovilmart
Copy link
Contributor

there you have it :)

user.fetch()
.then(() => {
expect(user.get('emailVerified')).toEqual(false);
expect(typeof user.get('_email_verify_token_expires_at')).toBe('undefined');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@flovilmart
Copy link
Contributor

@cherukumilli just 1 micro nit on the environment variable otherwise that looks more than awesome

@ghost
Copy link

ghost commented Jul 18, 2016

@cherukumilli updated the pull request.

@flovilmart
Copy link
Contributor

Awesome, just a note for the future, there is no need to squash your commits as we squash them upon merging :)

@cherukumilli
Copy link
Contributor Author

Thanks. I will not squash them going forward.

can you please re-run the build again?

@flovilmart
Copy link
Contributor

@drew-gross I'll let you merge as you reviewed the code more extensively

@drew-gross
Copy link
Contributor

Looks awesome to me. So many tests ❤️

@drew-gross drew-gross merged commit 6f29205 into parse-community:master Jul 19, 2016
@cherukumilli cherukumilli deleted the ability-to-expire-email-verify-token branch July 19, 2016 17:47
rsouzas pushed a commit to back4app/parse-server that referenced this pull request Mar 15, 2017
rsouzas pushed a commit to back4app/parse-server that referenced this pull request Mar 16, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants