Last reviewed: October 31, 2026 (Asia/Shanghai). Sources fetched October 30, 2026 UTC.
Direct answer
Review password-reset changes in coding-agent patches by tracing the entire recovery grant: who can request it, which account it authorizes, where the email link points, when it expires, how successful redemption prevents reuse, and what happens to existing sessions. Require a valid recovery grant before any password change. Test two simultaneous submissions of the same grant through the real persistence layer; sequential replay tests alone cannot establish single-use behavior under concurrency.
The OWASP Forgot Password Cheat Sheet calls for consistent request responses, secure expiring single-use tokens, trusted HTTPS reset URLs, and a deliberate session-invalidation policy. A working email and a successful form submission cover only part of that contract.
Consider an agent-written handler that checks used_at, changes the password, then marks the token used. Two workers can both pass the first check before either writes. The review must establish one successful credential transition across workers, including retries and failures between writes.
Who this is for
Use this guide when a coding agent changes forgot-password routes, email templates, token helpers, password-confirmation forms, user models, background mail delivery, or post-reset session handling. It covers application-owned password recovery through an established email address. MFA recovery, support-assisted identity proofing, and identity-provider account recovery require their own policies.
The example policy permits one successful redemption per grant, invalidates outstanding reset grants and existing application sessions after success, and requires ordinary sign-in afterward. Those are explicit acceptance criteria for this workflow. Confirm the appropriate recovery policy with the identity owner before implementing it.
Key takeaways
- A reset request must not reveal account existence or change credentials.
- Bind the grant to an account and recovery purpose; a submitted account ID cannot redirect its authority.
- Use the framework’s maintained token mechanism and password-setting API.
- Make validation, credential replacement, and grant invalidation consistent under concurrency.
- Generate email links from trusted origin configuration and prevent token leakage.
- Test existing sessions separately from the new password.
Sources checked
Three primary public references were fetched successfully on October 30, 2026 UTC:
- OWASP: Forgot Password Cheat Sheet supports request privacy, abuse controls, token lifetime and single use, recovery-link construction, password policy, notifications, and post-reset login decisions.
- Django: Using the authentication system documents password-reset eligibility, its email-timing warning, reset-confirmation behavior, and password-based session invalidation. The fetched canonical reference is versioned 6.1; match the documentation to the installed release when reviewing a project.
- OWASP: Session Management Cheat Sheet supports server-side session invalidation, session renewal around authentication changes, sensitive-page caching controls, and credential-safe logging.
These maintained technical references support an evergreen engineering task. The transaction outline and test matrix below are recommended acceptance checks derived from their requirements. They are not reported application test results or a newly disclosed incident.
Contract details to verify
Separate request acceptance from recovery authority
An unauthenticated request supplies an account lookup value. It does not prove ownership. Use the same public message and response shape for eligible and ineligible addresses, and review timing as well as wording. OWASP recommends consistent response timing; asynchronous delivery can help keep email transmission off the response path, but it does not automatically remove every existence-dependent branch.
Django documents a useful eligibility boundary: its reset view sends mail only for an existing active user with a usable password, while other cases still reach the completion page. A patch that enables password recovery for an externally authenticated account can change the account’s authentication model. Preserve the intended eligibility rule.
Rate-limit reset requests and token guesses, with controls that address repeated requests against one account as well as abusive sources. Throttle recovery attempts without locking the victim out of ordinary login merely because somebody requested a reset. Send recovery mail to the account’s established recovery address, never to an alternate delivery address supplied with the request.
Define the grant’s scope and lifetime
Record the following decisions before approving token code:
| Boundary | Required decision |
|---|---|
| Account | Trusted account identity associated with the grant |
| Purpose | Password recovery only; no general login or MFA-reset authority |
| Expiry | Explicit lifetime, clock source, and expiration boundary |
| Reuse | How success makes the grant unacceptable to every worker |
| Supersession | Whether a new request invalidates older unredeemed grants |
| Success | Password update, outstanding-grant policy, session policy, and notification |
Prefer the framework’s maintained reset mechanism. For a custom opaque-token design, use a cryptographically secure random secret and protect its stored representation. A one-way verifier is a useful engineering choice so a read-only token-table disclosure does not immediately expose redeemable secrets. Bind the record to the account, recovery purpose, expiry, and consumption state.
For a framework-provided token, review its documented invalidation mechanism rather than assuming it uses a token table. A signed token’s integrity and expiry still leave the separate questions of replay, concurrent redemption, and invalidation after account changes.
Make the credential transition indivisible
For a custom database-backed grant, the following is a transaction design to adapt and test:
- Validate the proposed password against the application’s normal policy without changing account state.
- Start a transaction and serialize competing redemptions for the affected account and grant using the database’s appropriate locking or conditional-update mechanism.
- Recheck account binding, recovery purpose, expiry, unused state, and any credential-version rule against authoritative state inside that protected transition.
- Set the password through the normal password API, consume the grant, and invalidate other outstanding grants according to the example policy.
- Commit those changes together. Arrange the success notification through the application’s reliable post-commit delivery mechanism.
The required invariant is that competing valid submissions cannot both commit password replacements. A lock in one web process cannot coordinate other processes. A conditional update is useful only if its result and the password update participate in the same protected transition. For a framework-generated token, inspect the integration’s equivalent account-level serialization and revalidation behavior; the documentation’s phrase “one-time use” does not by itself prove a custom wrapper’s concurrency behavior.
Keep email delivery outside the credential transaction’s success decision. If delivery fails after the password changed, retry the notification under the application’s delivery policy without reopening the consumed grant. If the credential transaction rolls back, it must not leave a consumed grant paired with an unchanged password. These are implementation recommendations, not guarantees supplied by the cited frameworks.
Keep the recovery link inside a trusted boundary
OWASP warns against constructing reset URLs from an untrusted Host header. Use a configured HTTPS origin or a validated trusted-domain mapping. In a multi-tenant product, derive that mapping from trusted tenant configuration rather than a caller’s requested destination. Test forwarded-host handling through the real proxy.
Apply Referrer-Policy: no-referrer on recovery pages, as OWASP recommends. Exclude raw reset URLs from analytics, error reports, access logs, and agent transcripts. Review third-party resources and sensitive-page cache policy as possible disclosure paths. Opening a link may validate the grant or establish narrowly scoped recovery state; it must not itself change the password or grant ordinary account access. In this example, final consumption accompanies the successful password submission, so a mail scanner’s preview does not spend the grant.
If the flow establishes a recovery cookie or session, review the password-submission route with the site’s CSRF review workflow . A recovery session should authorize only the intended recovery operation.
Verify session invalidation as its own outcome
OWASP recommends ordinary login after reset and either offering session invalidation or doing it automatically. This guide’s example chooses automatic invalidation. Inventory application sessions, remember-me credentials, refresh tokens, and any other credentials the product promises to revoke. Password replacement alone cannot establish revocation across independent stores or services.
The Django authentication reference
explains that compatible user models include a password-derived authentication hash in sessions and check it on subsequent requests. It also describes preserving the current session during an authenticated password change. Review that distinction before copying a password-change helper into an unauthenticated recovery flow. Django’s reset-confirmation view defaults post_reset_login to False.
For distributed revocation, specify how each consumer observes invalidation and what access is permitted while it is pending. Test the old credential against a protected endpoint after reset; deleting a cookie in the recovery browser does not invalidate a stolen copy elsewhere. A reset must also preserve the product’s MFA requirements and recovery-contact policy.
Happy-path operator workflow
Freeze the agent revision and trace request, mail generation, confirmation, credential update, and session checks. Use disposable accounts and a local mail sink. Capture the link privately, verify its trusted origin, submit a compliant new password through the real route, and confirm that the old password fails while the new password works through ordinary sign-in. Check the selected session policy and confirm that the notification contains no password.
Retain the revision, framework version, recovery-policy version, fixture label, result class, and credential-transition count. Keep tokens, passwords, session cookies, raw mail bodies, and full recovery URLs out of shared evidence.
Prove the boundary with a redemption matrix
Exercise the real database and middleware. For concurrent cases, use two independently valid recovery clients and a controlled synchronization point so both submissions reach the critical transition. An unrelated CSRF failure must not be mistaken for proof of single-use enforcement. The site’s race-condition testing guide explains how to retain reproducible ordering evidence.
| Fixture | Required observation under the example policy |
|---|---|
| Eligible account and valid grant | One password transition; ordinary sign-in required |
| Unknown, inactive, or externally authenticated ineligible account | Same public request response; no password or eligibility change |
| Altered grant or another account ID submitted with it | No unauthorized account mutation |
| Expired grant at the configured boundary | Rejected; password and consumption state unchanged |
| Successful grant submitted again | No second password transition |
| Same grant submitted concurrently with two different new passwords | Exactly one commit; losing request cannot overwrite it |
| Two different outstanding grants redeemed concurrently | One succeeds; the other fails under the invalidate-all-grants policy |
| Failure injected between password write and grant consumption | Transaction rolls back both changes |
| Notification delivery fails after commit | Password remains changed; grant stays consumed; delivery can retry |
| Host or forwarded-host altered during issuance | Mail contains only the configured trusted recovery origin |
| Recovery link opened by a preview client | No password change, ordinary login, or final grant consumption |
| Session created before reset reused afterward | Protected request denied under the selected revocation policy |
Use an injectable clock for expiry cases. Inspect durable state and the final accepted password after both competing requests complete. A pair of response codes alone cannot show which writes committed. Compare request-timing distributions for known and unknown accounts over repeated controlled samples; one matching response time proves little.
Error-path operator workflow
For malformed, expired, consumed, or unbound grants, return a controlled generic recovery failure before changing credentials. If authoritative recovery state is unavailable, stop the credential transition. Do not fall back to an unchecked token or a user ID from the form.
If the response is lost after a successful commit, a repeat must find the grant unusable. Guide the user to ordinary sign-in or a fresh recovery request without repeating the credential update. If a negative fixture changes a password, preserves forbidden access, or leaks a usable link, block the patch, preserve sanitized evidence, and repair that boundary before rerunning the matrix.
Failure modes
Identical wording hides different behavior. Status codes, redirect targets, eligibility changes, and email-dependent timing can still disclose account information.
Token validation and consumption surround separate writes. Two workers or a mid-write failure can violate single use. Verify the complete credential transition.
A password-reset helper becomes a general recovery bypass. Preserve external-authentication eligibility, MFA requirements, and restrictions on changing recovery contacts.
The new password works, so recovery is declared complete. Old sessions or independent refresh credentials may still authorize access. Exercise those consumers explicitly.
FAQ
Does a signed JWT make a reset grant single use?
Signature verification establishes integrity under the selected key and policy. It does not record redemption. The design still needs an authoritative consumption or account-state rule that remains correct under concurrency.
Should requesting a second email revoke the first link?
Choose and document that policy. Revoking on every unauthenticated request can let another person invalidate a legitimate user’s link repeatedly. Regardless of issuance policy, this example invalidates all outstanding grants after a successful password reset.
Does a password reset recover a compromised account completely?
It may leave changed recovery contacts, unauthorized MFA methods, or independent sessions in place. OWASP treats suspected-compromise recovery as a broader process. Route that case through the reviewed account-recovery policy.
Reader next step
Choose one agent-written recovery handler. Write its account, purpose, expiry, redemption, and session contract, then add a concurrent same-grant test and an old-session test through the real stack. Approve the patch when a legitimate user can recover and the recorded negative cases cannot overwrite credentials, reuse authority, or retain access the policy promises to revoke.