Back to blog

Account recovery is an authentication system


Own Auth password recovery uses a generic request response, a rate-limited recipient bucket, a single-use token, and full session revocation after the password changes.

Keep the request endpoint uninformative

auth.requestPasswordReset returns successfully for an unknown address with an internal expiresAt: null result. The route must not serialize that result; return the same status and body for known and unknown accounts. OWASP also recommends avoiding large timing differences between the two paths.

Own Auth permits five password-reset requests per normalized address every fifteen minutes. Network limits can separately constrain one source targeting many addresses. A request alone must not lock the account, revoke sessions, or change account state because the caller has not presented a recovery secret.

Make the link a narrowly scoped credential

A reset token should authorize exactly one operation for exactly one account. It must be generated with a cryptographically secure random source, stored in a protected form, expire quickly, and become unusable after the first successful reset. The reset URL must use HTTPS and an application-controlled origin. Never build it from an untrusted Host header.

The reset page should exclude third-party scripts that could capture the URL and use a restrictive referrer policy. Request logs, traces, error reports, and product analytics must omit the token and complete reset URL.

Own Auth creates a single-use password-reset token, stores only its peppered hash, and uses a one-hour lifetime by default. auth.resetPassword checks the token type and expiry, atomically consumes it, applies the configured password policy, and hashes the replacement password. Expired tokens produce expired_token, consumed tokens produce token_already_used, and malformed or wrong-purpose values produce invalid_token. The atomic update admits one successful reset when requests arrive concurrently.

End the old security context

When auth.resetPassword succeeds, Own Auth revokes every existing session and does not create a replacement. A successful reset records password.changed and session.revoked_all; the revocation event includes reason: "password_reset" and the number of sessions revoked. The user signs in with the new password. Applications that issue separate API keys, remembered-device credentials, or OAuth grants need an explicit recovery policy for those credentials because session revocation does not imply their removal.

Atomic consumption needs a concurrent test, not only a sequential replay. Submit the same valid token through simultaneous auth.resetPassword calls. Exactly one call must change the password; every competing call must fail without applying another change. The successful path should also leave all previous sessions revoked and no replacement session created.

Factor recovery needs stronger evidence

Resetting a forgotten password through a previously verified email address is different from recovering an account after every enrolled factor has been lost. NIST treats account recovery as an authenticator lifecycle event and describes saved recovery codes, issued recovery codes, recovery contacts, and repeated identity proofing as distinct methods. The right combination depends on the account's assurance level and the consequences of takeover.

Do not fall back to security questions, public profile details, an old password, or information a support agent can find online. For privileged or high-value accounts, use documented evidence requirements, limited support permissions, independent approval, and a cooling-off period before ownership transfers or payout changes. A support conversation should never become an improvised identity-proofing protocol.