A password change is an account-recovery decision disguised as a settings form. Short answer: require fresh proof of identity, score the device and request context, then revoke or retain sessions according to an explicit risk policy. A logged-in cookie alone is not enough proof for a high-impact change.
In a developer-tools product, I care about the recovery path because a false positive can lock out the person who owns a build pipeline, while a false negative hands that pipeline to an attacker. The useful unit to evaluate is not “did the request authenticate?” but “what evidence should permit this credential transition, and what sessions survive it?”
What evidence should permit an authenticated password change?
Start with a step-up transaction, separate from the ordinary session. Ask for the current password when it is available, require a phishing-resistant factor for privileged accounts, and bind the approval to the user, device fingerprint, and a short expiry. OWASP’s Authentication Cheat Sheet calls for reauthentication after risk events and recommends invalidating sessions after a password reset; an authenticated change deserves the same deliberate treatment.
Device fingerprints are signals, not identity. A familiar browser can still be stolen, and a new browser can be the legitimate owner on a trip. I feed those signals into a small risk record rather than letting one score silently decide access:
from dataclasses import dataclass
@dataclass
class ChangeContext:
current_password_ok: bool
mfa_ok: bool
device_risk: float
recovery_channel_recent: bool
def require_step_up(ctx: ChangeContext) -> str:
if not ctx.current_password_ok:
return "deny_and_offer_recovery"
if ctx.device_risk >= 0.70 and not ctx.mfa_ok:
return "require_mfa"
if not ctx.recovery_channel_recent:
return "delay_and_notify"
return "allow_change"
The thresholds are policy placeholders, not universal truth. Your mileage may vary; calibrate them against an eval set containing travel, new devices, password-manager use, and confirmed account takeover. I've found that a seven-minute step-up window is easier to reason about than a vague “recently authenticated” label, but that duration still belongs in your threat model. I once assumed a binary trusted-device flag would be enough. It produced a clean notebook chart and a messy recovery queue once mobile clients rotated identifiers, so the production policy had to record evidence age, factor type, and the exact device-binding event instead of trusting a single boolean.
Keep it boring.
How should existing sessions behave after the credential transition?
Make the default explicit: revoke every refresh token and long-lived session, preserve only the transaction that completed the change long enough to show confirmation, and force fresh authentication elsewhere. This closes the common gap where an attacker keeps a stolen session after the password is replaced.
There are legitimate exceptions. A user changing a password while fixing a local typo may reasonably remain signed in on that same device, but that exception should require recent step-up proof and a low-risk context. Never preserve sessions created before a suspicious recovery event merely to reduce support tickets. The catch is operational: revocation can interrupt CI agents and IDE integrations, so provide a clear re-login path and audit the event with actor, device, reason, and token family identifiers.
Session handling should be transactional. Write the password hash, increment a credential version, and invalidate token families in one durable operation. On every request, compare the token’s version with the account version. If the write partially fails, the old credential and its sessions must remain valid; an ambiguous half-change is worse than a rejected change.
Where do risk scores fail in real recovery flows?
The first failure mode is a score without a counterfactual. Measure what would happen if the same person used a new laptop, lost a security key, or changed a password during an incident. In a replay, I separate the event that raised the score from the event that justified the decision: an IP change may be noisy, while a new recovery address plus a missing factor is a materially different combination. Track false lockouts separately from account-takeover catches; optimizing one metric can quietly damage the other. Keep the raw signals behind access controls, retain only the features needed for review, and test the policy again whenever a client release changes fingerprint stability.
The second is notification theater. Send an out-of-band alert after the change, but do not treat delivery as proof that the requester is the owner. Recovery email, help-desk approval, and backup codes each need their own assurance level and cooldown. A support agent should see the policy outcome and evidence age, not a raw fingerprint string.
For an eval harness, store anonymized events and replay the policy in Python. Keep prompts and explanations short when an AI agent summarizes a case: token cost matters, and a concise reason code is easier to review than a full request dump. Log the model version, policy version, and final human decision so a changed prompt cannot rewrite history.
A practical decision rule for shipping
Ship the narrowest rule that your recovery team can explain: fresh proof for every password change, stronger proof when device risk is high, and broad session revocation unless the same transaction has earned a narrowly scoped exception. Do not ship a threshold you cannot monitor.
This approach is not suitable when your product cannot offer any independent recovery factor or durable token revocation; in that case, stick with a slower manual recovery flow and accept the support cost. It is also a poor fit for anonymous accounts where there is no stable identity record to bind to a device.
Before copying the rule, measure step-up completion, recovery time, session-revocation lag, false-lockout rate, and confirmed takeover rate over representative test cases. The numbers tell you whether the policy protects users or only makes the dashboard look reassuring.
References
- OWASP Authentication Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html
- RFC 9449, OAuth 2.0 Demonstrating Proof of Possession: https://www.rfc-editor.org/rfc/rfc9449
- NIST Digital Identity Guidelines: https://pages.nist.gov/800-63-3/
Further reading
- OWASP Authentication Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html
- RFC 9449: https://www.rfc-editor.org/rfc/rfc9449
Top comments (0)