# Engineer a Safe Password-Reset Link Lifecycle

> Handle retries, scanners, multiple requests, expiry, successful reset, and sessions as one recovery-state machine.

Canonical URL: https://www.devobs.io/articles/password-reset-link-lifecycle/
By: Ines Costa
Published: 2024-02-29T17:15:34.597Z
Updated: 2026-09-06T10:18:15.722Z
Section: Identity

## Treat reset links as one-time recovery grants

Generate a high-entropy token, store only its hash, bind it to an account and purpose, expire it, and consume it once. Return uniform request responses and avoid changing the account until the holder confirms on a safe page. [OWASP Forgot Password Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Forgot_Password_Cheat_Sheet.html) supplies the primary specification or operating model for this decision.

## Protect reset consumption from scanners and retries

Link scanners may visit URLs automatically, so GET displays a confirmation step and POST consumes the token. Decide whether a new request invalidates older links. After success, invalidate recovery grants and offer or require session revocation according to risk. [Ory account recovery documentation](https://www.ory.com/docs/kratos/self-service/flows/account-recovery-password-reset) provides the complementary protocol or operational detail.

## Model recovery as states

Store a hash of the reset token with account, purpose, created time, expiry, attempt count, and consumed time. A GET request may display confirmation but must not consume the grant because email security scanners visit links automatically. Consume it only on the password-changing POST, in the same transaction that updates credential state.

Choose and document whether a new request invalidates earlier grants. Uniform request responses limit account enumeration, while per-account and network controls slow abuse. Logs should contain a grant identifier or hash prefix, never the token.

## Resolve concurrent and post-reset behavior

Two submissions with the same link must have one winner. The loser receives an expired-or-used result without learning more account state. After success, invalidate remaining recovery grants, notify the account through an independent channel, and apply the product’s documented session policy. Ory Kratos provides self-service account recovery and management flows as described in the [Ory recovery documentation](https://www.ory.com/docs/kratos/self-service/flows/account-recovery-password-reset).

Test scanner GET, expired token, two active links, simultaneous POSTs, password-policy rejection, and a database failure during consumption. Start by drawing the state machine and adding a concurrency test that proves password update and one-time consumption cannot diverge.

## Source references

- <https://cheatsheetseries.owasp.org/cheatsheets/Forgot_Password_Cheat_Sheet.html>
- <https://www.ory.com/docs/kratos/self-service/flows/account-recovery-password-reset>
