-
-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Implement Email Verification #583
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
Conversation
@@ -0,0 +1,27 @@ | |||
function verifyEmail(appId, serverURL) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To keep with the current pattern, maybe we should have an class EmailVerificationController extends PromiseRouter {}
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The email related routes aren't really grouped together in any logical way, so I'm not sure that makes sense.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
your call, but it smells the need of decoupling the invalidLink, and verify success calls too, maybe on an adapter.
The EmailVerificationController would have the validation route, then the adapter would provide the behaviour for invalid link and success.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah some parts of those are potential customization points. I think it might make more sense as a separate adapter though.
If a class is never overridden, doesn't inherit any meaningful behaviour, and doesn't have any instance-specific data, I don't see any need to put it in a class. Classes are also slower due to chasing pointers around the prototype chain, if you have few instantiations and many calls (this applies to our controllers)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. Let's leave it like that for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually feel strongly here - we should be creating a MailController here, since we need a databaseAdapter here, which is state of a controller, which is not injected right now - but we are definitely moving that direction.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not 100% sure I follow
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you look at it right now - it could a simply function, sure, but as we are trying to decouple and move away from using globals - the DatabaseAdapter
piece as a first line in the function is actually a global, and should be injected at construction time of a controller - since it is required for it to function.
The idea is to have a single DatabaseAdapter per server, which is what we allow right now, and stop doing things like getDatabaseConnection
from a global.
Which at this point will make sense to move this entire piece into UsersController
, which will make it much simpler to extend.
Also, we might as well split some logic that deals with request/response here into UsersPublicRouter (because you don't need app specific security and friends).
import RestWrite from '../RestWrite'; | ||
import { newToken } from '../cryptoUtils'; | ||
import RestWrite from '../RestWrite'; | ||
let cryptoUtils = require('../cryptoUtils'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Use ES6 import.
@drew-gross updated the pull request. |
@@ -150,6 +170,7 @@ export class UsersRouter extends ClassesRouter { | |||
this.route('POST', '/requestPasswordReset', () => { | |||
throw new Parse.Error(Parse.Error.COMMAND_UNAVAILABLE, 'This path is not implemented yet.'); | |||
}); | |||
this.route('POST', '/requestPasswordReset', req => this.handleReset(req)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this might be coming, but is currently missing, just putting the comment here to remind :P
79a5238
to
0a982b7
Compare
@drew-gross updated the pull request. |
|
||
if (options.module) { | ||
//Configuring via module name + options | ||
return require(options.module)(options.options) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should check if require(options.module).default
for ES6 exported objects, is set, if set and is function, should create a new instance.
@drew-gross for the load adapter, es6 will break if you try to call Class() without the new operator. Your load adapter design is factory oriented. That can work too, but so far all the adapters we have are not factory oriented but class/instance oriented.
|
@@ -0,0 +1,39 @@ | |||
import Mailgun from 'mailgun-js'; | |||
|
|||
let SimpleMailgunAdapter = mailgunOptions => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, you still need to return an Object in the end, I'm not sure of the performance advantage of returning the object as you do instead of doing
export class SimpleMailgunAdapter {
constructor(options) {
this.mailgun = Mailgun(mailgunOptions);
}
sendMail(to, subject, text) {}
sendVerificationEmail({ link, user, appName, }) {}
}
This way you can use the default loadAdapter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typically, using classes is faster to instantiate (due to less allocation for the functions), using objects is faster to call methods (due to not needing to walk the prototype chain). Since these objects are called far more often than they are instantiated, this way should be better for perf.
@flovilmart the ones we've done so far are pretty much ad-hoc and there are only 3 of them. I'd rather pick a good interface now and stick with it for the future, than stick with a bad interface for consistency with the earliest 3 adapters. Also, it's pretty easy to accept legacy config options, for backwards compatibility, so it would be fairly easy to convert the 3 existing adapters to the new style while not forcing old servers to upgrade. I didn't account for classes because it's slower if you are only instantiating the object once (this should be true of most/all adapters) and I'm just generally against classes. Douglas Crockford explains why pretty clearly in this talk: https://www.youtube.com/watch?v=PSGEjv3Tqo0 |
Do we use it for public-facing adapters only? Do we use this as a rule of thumb everywhere? I agree to any approach you guys take, because I hope @drew-gross, you have a better idea on what makes best sense in the latest modern JavaScript that will scale as we go. The only thing that I don't see how it fits - injected state that is required for most/all methods of Controllers and the state that should live on the Controllers themselves. For example, FilesController requires a FilesAdapter, which is a parameter on the instance, for most of it's operations. I understand an extra call implication of performance here, but I don't think it actually regresses the performance by much (the stats that I found actually suggest the same performance implication). |
So far we have no concept of a non-public facing adapter, so I'm not sure what that means.
Or, like Douglas Crockford is advocating, object oriented without classes :)
How you construct the adapters is not related to where the state lives. Classes are also not required for injected state.
True, for shallow inheritance trees on modern JS engines the difference should be minimal. |
In the end, the goal is the flexibility for the 3rd party modules. In an ideal world, I'd support both options from a single loadAdapter. What we can do is: If it's an es6 module, (default is set when calling require) create an instance. Otherwise, just call the method with the options. Any way to tell at runtime the difference from a constructor function from a factory? Size of the prototype property maybe? |
I would somewhat disagree with this. I would suggest that we would rather there be exactly one way to implement adapters, and it should be lightweight and low-overhead, to reduce confusion and reduce the learning curve for people making many adapters (I'm envisioning a future where most extensibility is provided through adapters, so most parse app writers will be making at least a handful of them). |
If we did want to detect constructor requiring |
@drew-gross that seems reasonable, and I checked the babelified code and it injects the proper check:
Do you think you can try merging your adapter paradigm with .module, .class etc.. with the loadAdapter that takes care of instantiation + the trying? |
Yeah I can do that. I have a lot on my plate right now and I'm also sorta sick this week so it probably won't be soon. I'll try to get it in for 2.1.3 though. |
@drew-gross I can take over from where you are :) Get well, that's what matters! I'll PR on your branch. |
Cool, thanks. Please try to include some tests that demonstrate what the users of the interface will see, like I've done in |
No problem, I had a full test suite on the loadAdapter (100%) for demonstration purposes too :) |
Yeah that one didn't show what it looks like when you actually instantiate the |
watched Douglas's talk, very insightful! This pattern makes a lot of sense, should we move towards that kind of architecture? I actually may use it in Swift too. |
Talked to @drew-gross off GitHub and yeah - this is the future! Don't think we need to move everything right away, but rather - move as we go, so we don't spend time moving stuff around, but actually building things. Swift: you actually don't need to, since Seift inherently has this, re closures/functions/classes/structs/protocol extensions. |
Hah, @nlutsenko was just saying that this architecture makes a lot of sense in swift because scopes are closures in swift (apparently - I've probably written no more than 50 lines of swift yet) |
just the stupid let that means const and var that mean let... besides that we're good :) |
@nlutsenko started writing an express like server in swift, based on protocol extensions just as an experiment. That actually is very nice code. |
Closing as superset by #627 |
Still missing:
Password resets
Setting emailVerified to false and sending a new verification email when email is changed after creation.
Use of AdapterLoader interface.