Not op, but I've written several apps with a similar setup to what was described. Both email verification and password resets are pretty simple to implement yourself.
For password reset, you just create a record with a unique token and send an email that links back to the app with the unique token in the url.
Email verification is basically the same: send an email with a link that identifies the user and hit the server with the unique token when that page loads.
The hardest thing is probably making sure you're appropriately using an appropriate api for generating unpredictable tokens. Generating random tokens is a trade off between speed and unpredictability and some easy-to-find random number apis make the wrong trade off.
Most any language's built in psuedorandom number generator is going to be sufficiently random that you will have no trouble.
I mean, you could take something as facile as the sha1 of the current microtime, and a random concatenation of the user's data from the user table and that would already require so much access that figuring out the token wouldn't even be your biggest problem.
> Most any language's built in psuedorandom number generator is going to be sufficiently random that you will have no trouble.
I once collected a $3k bug bounty over this. Python's use of Mersenne Twister in the lib/random module should not be used for token generation. Mersenne twister uses a relatively small state space and is fully deterministic (it never re-seeds or mixes in new entropy). If you get a couple sequential random values you can reconstruct that state space and predict all future values. I.E. request a password reset 10x in a row and examine the tokens in the emails.
Please only use secure random number generators when creating security related tokens.
Many mainstream languages have separate cryptographically secure rngs. The standard built-in rngs have tons of flaws for crypto work. A plausible attack vector here is something akin to a chosen plaintext attack -- request a stream of password resets to accounts the attacker controls to find the current state of the prng. Usually a couple readings suffice for the language builtins to uncover the rest of the random stream. With that newfound knowledge, it's game over, and the attacker controls the password reset links for _anyone_ requesting a reset -- polling the future reset links to account for other uses of the language's rng and to keep the attacker's internal rng stream in sync with your service.
Other flaws exist like abysmally poor key spaces. If your prng has a period of 64k and the reset links are generated deterministically from the prng, you're going to have a bad time.
The sha1 of microsecond+userdata is interesting. It has the potential to work well, but it's easy to get wrong. Latency measurements, the framework you're using, and other pieces of information can reduce microsecond timings to a few bits of entropy (e.g. there are modern systems that can only measure time aligned to 15millisecond boundaries). Once you take out the PII (most of which the attacker has access to already, so it isn't buying additional entropy), in systems I've seen there isn't that much real entropy in user state (sometimes under 12 bits even with tens of millions of users), and users who haven't interacted with your system much will have much less. If your system is closed source you might buy some security through obscurity, but that never lasts, and the underlying crypto is _probably_ flimsy at best.
It wouldn't take that much effort to go through my claims and find special cases where the strategies would work well, find workarounds for the attacks mentioned, and whatnot. That isn't really the point though. What matters is that getting this right is hard, and even a system which looks good enough might have subtle flaws that render its security all but useless. Maybe if we went back and forth enough we'd find all the problems, but there are already battle-tested solutions that are almost certainly better than anything we're going to come up with here, and in any application where security matters, ignoring those drop-in solutions is probably the wrong choice.
That said, it might very well be the case that having a certain percentage of user accounts compromised is an acceptable trade-off (or even desirable? could you then charge people to monitor their accounts for suspicious activity à la Equifax?). I think that's a choice that should be made consciously though, not as an afterthought arising from a broken security model.
I agree with everything you said. Use what the professionals have created. My point is just that some of these facile methods like my example are worse implementations and should be upgraded. But they aren't completely useless and just as bad as a 4 character password stored in plaintext on the server.
Which is how some people seem to approach security advice... "either it's up to my ideal standard, or it's a completely idiotic implementation that will surely be hacked in a fortnight."
You seem to have some balance and I applaud that. Security is a balancing act between the level of security, development and maintenance difficulty, and user experience and you have to negotiate an acceptable level that at least exceeds the bare minimum of security required.
For password reset, you just create a record with a unique token and send an email that links back to the app with the unique token in the url.
Email verification is basically the same: send an email with a link that identifies the user and hit the server with the unique token when that page loads.