Have a user-configurable boolean value for "reset allowed".
If you have their e-mail / some out-of-band means of communicating with the end user
When the user clicks the "I forgot my password" link, if "reset allowed" is true, send a password reset link to the e-mail account associated with the user account. Make sure you use an e-mail service that first attempts end-to-end encryption (so that, if their mail server supports that, the link never gets sent in plain. And if their server doesn't support that, they aren't denied the link).
Rate limit the number of times that e-mail can be sent to the same account (e.g. 2 resets per day) so someone can't use that to spam the account. Don't let the user know if an e-mail has been sent or if the account they mentioned even exists.
If they don't click the password reset link, nothing should change. If they click it after a certain timeout period, or if between the e-mail being sent and the time of it being clicked "reset allowed" has been set to false, nothing should happen.
If you don't have an out-of-band means of communication
If "reset allowed" is set, use a challenge-response mechanism such as user-configurable security questions. If they answer the questions correctly, allow them to enter a new password. You might also want to consider hashing the responses.
You could, of course, also use something like TOTP, but chances are if they don't have the password, they also won't have the key needed for this.
Either way
Conduct the entire exchange over an encrypted channel. Unless you're trying to stop the NSA from stealing credentials, SSL should be enough.