parse-community / Parse-Swift

The Swift SDK for Parse Platform (iOS, macOS, watchOS, tvOS, Linux, Android, Windows)
https://parseplatform.org
MIT License
304 stars 69 forks source link

Verification Emails are triggered every time User.current is saved #239

Closed dblythy closed 2 years ago

dblythy commented 2 years ago

New Issue Checklist

Issue Description

Parse Swift triggers email verification whenever User.current?.save is called.

Steps to reproduce

  1. Turn verifyUserEmails on.
  2. Update any field on User.current
  3. Check emails

Actual Outcome

Emails are sent every time user is updated

Expected Outcome

Emails to only send on signup, or email update, as per the SDK.

Specifically, the issue seems to be related to here o the server:

RestWrite.prototype._validateEmail = function () {
 if (!this.data.email || this.data.email.__op === 'Delete') {
    return Promise.resolve();
  } 

For the JS SDK, this.data.email is undefined on any update. For Parse Swift calls, this.data.email is passed.

Environment

Client

Server

Database

Logs

parse-github-assistant[bot] commented 2 years ago

Thanks for opening this issue!

cbaker6 commented 2 years ago

In the JS SDK, are you able to show where the following occurs:

For the JS SDK, this.data.email is undefined on any update

That will provide some guidance on where this is happening on the client level, or if the server is actually handling it.

dblythy commented 2 years ago

I'm probably not familiar enough with the JS SDK to point where exactly it happens, but my understanding is that object.set(key) add the key to a list of pending OPS, which is then sent to the server, and these specific keys are set. I detected this by console.logging this.data in RestWrite.js. As far as I can tell in my PR, Parse Swift sends the whole body to the server.

I would assume it's _getSaveJSON, which determines what JSON objects need to be sent to the server, only if they have been mutated.

dblythy commented 2 years ago

Another way to replicate this is to have a simple beforeSave trigger on Parse.User, and log object.dirtyKeys(). Even with the code User.current?.save{ result in }, dirtyKeys is the entire struct for Parse Swift. Unless operaton?.save{ is used, which seems to only set the proper key.

cbaker6 commented 2 years ago

I would assume it's _getSaveJSON, which determines what JSON objects need to be sent to the server, only if they have been mutated.

I see, this may be the reason. Since ParseSwift uses structs it doesn't have getters/setters and doesn't have a notion of dirty. If the PR I just openned doesn't work, I think I may have a solution.

dblythy commented 2 years ago

Thank you for your help @cbaker6, it is much appreciated.

The issue still occurs on that branch too.

emailVerified isn't showing up in either master or this branch as a dirtyKey, the dirtyKeys are set as 'username', 'email', 'firstName', 'lastName', 'ACL'.

I'm assuming the emailVerified is being triggered by server thinking email is being changed

dblythy commented 2 years ago

Some alternative options to make sure dirtyKeys is passed properly:

  1. a cloud function that reverts database ops if original.get(key) === object.get(key)
  2. special internal handling for ParseSwift to manage these dirtykeys

Option 1 would require every class which is used in Parse Swift to have a beforeSave trigger. Option 2 would require server changes and no Parse Swift changes.

cbaker6 commented 2 years ago

@dblythy thanks for reporting! I believe the linked PR fixes the issue you found.