Short answer: make the forgot-password endpoint behave the same for a known and unknown address, then let a single-use, short-lived token move the account into a confirmed reset state before revoking sessions. For a marketplace migrating off a managed auth provider, keep that state machine in your application database and keep email delivery behind a replaceable interface.
I build Python services that move from notebook experiments to production, so I care about two things in this flow: an evaluator can prove each transition, and a retry cannot spend a token twice. Bot signups and forgotten passwords are different abuse problems, but they meet at the same trust boundary: an email address is not proof of identity until the user completes a challenge.
The pipeline starts with an intentionally boring response
The request flow is small enough to draw on a whiteboard. A browser posts an email address. The API normalizes it, records a hashed recovery request, and queues a message if an account matches. The HTTP response is identical either way. A link carries a random token, never a user id or email in the URL. The reset endpoint hashes the token, consumes it in one transaction, changes the password, and revokes every active session for that account.






