Skip to content

feat(security): password reset flow (request/confirm + session revocation) - #290

Merged
izzywdev merged 4 commits into
masterfrom
claude/impl-password-reset
Jul 17, 2026
Merged

feat(security): password reset flow (request/confirm + session revocation)#290
izzywdev merged 4 commits into
masterfrom
claude/impl-password-reset

Conversation

@izzywdev

Copy link
Copy Markdown
Owner

Implements POST /session/password/reset-request and /session/password/reset-confirm — in the frozen contract (#273) but 404 in production; never implemented.

⚠️ UNVERIFIED — tsc and jest have never run against this. The authoring agent's isolated-cache npm install timed out twice, then plan mode froze it. 19 tests exist and have executed zero times. CI is the gate here; do not read this PR as a claim that it works.

Design

  • Reset token: sha256-hashed at rest, single-use, 30-min TTL, idempotent migration (013_create_password_resets).
  • Password is set in Authentik via the Admin API — no bcrypt, no local password row. Authentik remains the credential store.
  • All sessions revoked on reset. Now that fix(security): enforce session revocation on the request path #282 makes authenticateToken enforce the sessions table, deleting those rows genuinely logs the attacker out — a reset that left old sessions alive would be theatre.
  • Resolves the target by email then re-matches exactly (case-insensitively), because Authentik's ?email= is a filter, not an exact match — resetting the wrong account would be unacceptable.

Three decisions worth scrutinising

  1. No enumeration oracle: unknown email, unwired email channel, and dispatch failure all return an identical 202. The route deliberately swallows provider throws — a 5xx for real accounts only would itself leak existence. Only a malformed body 400s.
  2. Confirm ordering: set password → consume token → revoke sessions. A store/policy rejection leaves the token live so a compliant retry works.
  3. Degrades + logs when the email channel is unwired (mirrors emailVerificationEnabled()), rather than stranding the user.

hold — deploy-on-push; ruleset requires 1 approving review.

Co-Authored-By: Claude

fuzeone-bot Bot and others added 2 commits July 17, 2026 07:29
… + reset notification [skip ci]

Co-Authored-By: Claude <claude-opus-4-8> <noreply@anthropic.com>
Claude-Session-Id: cf830721-b1ef-4fe0-a024-035ad280dcf7
…tion) [skip ci]

Co-Authored-By: Claude <claude-opus-4-8> <noreply@anthropic.com>
Claude-Session-Id: cf830721-b1ef-4fe0-a024-035ad280dcf7
@izzywdev izzywdev added the hold label Jul 17, 2026
…ed this)

Co-Authored-By: Claude <claude-opus-4-8> <noreply@anthropic.com>
Claude-Session-Id: cf830721-b1ef-4fe0-a024-035ad280dcf7
@izzywdev izzywdev removed the hold label Jul 17, 2026
@izzywdev
izzywdev merged commit f0195ab into master Jul 17, 2026
31 of 33 checks passed
@github-actions
github-actions Bot enabled auto-merge (squash) July 17, 2026 06:06
@izzywdev
izzywdev deleted the claude/impl-password-reset branch July 17, 2026 06:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant