Feature: Password reset links should work until TTL expires or user’s password is reset
Current/Problematic Behavior:
Every time a user submits a password reset request, all previous password reset links are invalidated, such that only the very latest link works.
This is a common source of confusion for users for a couple reasons:
- Some popular email clients (notably Gmail) group emails with the same sender & subject into a threaded “conversation” view, where the oldest email received is at the top, with subsequent emails listed below. In these clients, users naturally have a tendency to see/open the earlier emails first, but only the final email at the bottom of the thread will contain a working link. This problem is compounded by the fact that Gmail abbreviates later emails to eliminate redundant verbiage, so in the case of redundant password reset emails, where the only difference is the link coming from an HTML button, then Gmail hides the content of later emails altogether, and the user must know somehow that they need to click the “…” to reveal content of the email that actually contains a working link:
- Although it is common for emails to arrive within seconds, there can be various delays between the sending of an email and its actual delivery into a user’s inbox. When a user requests a password reset, then checks their inbox and doesn’t see the email yet, then they are very likely to request to reset their password again. By this point, the first email may have arrived, but the link will have been invalidated by the second request, so they may be tempted to try to reset once more. This can lead to a frustrating cycle where the user believes they are clicking on the most recent email, but in fact are seeing the penultimate email sent at that point.
This issue was previously raised in How to make email links work even for previously sent password reset emails ? (only the last link sent works), where the response given was:
The link in every email has a unique ticket number which is for securing the request. We can not disable this security feature.
My feedback here does not intend to change this behavior.
However, that response also linked to the Change Users' Passwords documentation, which currently says:
If the user receives multiple password reset emails, only the password link in the most recent email is valid.
My response/feedback to this is — why? Why is unacceptable for a password reset link sent minutes earlier to be used instead of the most recent one? What is the threat model that this protects against?
Description/Proposed Solution:
My request is to change (or provide an option to change) the current behavior such that password request links (i.e. tickets) will remain valid for the duration of their TTL, unless/until the user’s password has been reset. This behavior would retain the protection that at most one password request link can be used — once the user’s password has been reset, then all previously generated password reset tickets would be invalidated.
This behavior should also be fairly simple to implement, at least in theory. Every user has a last_password_reset
property that can be compared against the (internally-known) creation time of the password reset ticket, to enforce that tickets generated prior to the reset are no longer valid.
This behavior would substantially improve the UX surrounding password reset flows, leading to reduced problems/support tickets to your client’s support teams, thereby making clients (like us) happier.
Additional Benefit(?)
Missing "Back to My Application" Button on Expired /u/reset-password Page states that:
When a new ticket is created, older tickets of the same type are deleted for the given user.
The behavior I’ve described would be implemented as a logic change on the reset password page itself, which would obviate the need for deleting older tickets described here. As a result, this behavior change could have the added benefit of preserving all relevant information from the password reset ticket, ensuring that the reset password page retains all necessary information for rendering a link back to the original application, even in the context of expired links.
Use-case:
I’d like users who accidentally request to reset their password multiple times in short succession to be able to use any (recent) link they receive to successfully reset their password, and thereby log into our applications with minimal frustration.