fix(auth): keep recovery code valid after password change #82

Merged
gertjan merged 1 commit from claude/wonderful-perlman-083ccd into main 2026-07-10 11:51:38 +00:00
Owner

change-password rotated dek_salt but left recovery_encrypted_dek
untouched, while reset-password derived the recovery KEK from the
current dek_salt. Any password change therefore permanently broke the
recovery code: the derived KEK could never match the one that wrapped
recovery_encrypted_dek, and reset-password always failed InvalidToken.

Fix: give the recovery wrap its own salt.

  • register derives the recovery KEK from a new recovery_dek_salt field
    that never rotates; dek_salt remains free to rotate on password change
  • reset-password derives the recovery KEK from recovery_dek_salt
  • legacy docs without the field fall back to dek_salt on read (their
    recovery KEK was derived from it), and change-password materializes
    that fallback before rotating the salt out from under it
  • regression test drives register -> change-password -> reset-password
    through a stateful repo mock that actually persists credential updates

Co-Authored-By: Claude Fable 5 noreply@anthropic.com

change-password rotated dek_salt but left recovery_encrypted_dek untouched, while reset-password derived the recovery KEK from the current dek_salt. Any password change therefore permanently broke the recovery code: the derived KEK could never match the one that wrapped recovery_encrypted_dek, and reset-password always failed InvalidToken. Fix: give the recovery wrap its own salt. - register derives the recovery KEK from a new recovery_dek_salt field that never rotates; dek_salt remains free to rotate on password change - reset-password derives the recovery KEK from recovery_dek_salt - legacy docs without the field fall back to dek_salt on read (their recovery KEK was derived from it), and change-password materializes that fallback before rotating the salt out from under it - regression test drives register -> change-password -> reset-password through a stateful repo mock that actually persists credential updates Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
fix(auth): keep recovery code valid after password change
All checks were successful
Deploy / check (pull_request) Successful in 5m4s
Deploy / deploy (pull_request) Has been skipped
c28241629a
change-password rotated dek_salt but left recovery_encrypted_dek
untouched, while reset-password derived the recovery KEK from the
current dek_salt. Any password change therefore permanently broke the
recovery code: the derived KEK could never match the one that wrapped
recovery_encrypted_dek, and reset-password always failed InvalidToken.

Fix: give the recovery wrap its own salt.

- register derives the recovery KEK from a new recovery_dek_salt field
  that never rotates; dek_salt remains free to rotate on password change
- reset-password derives the recovery KEK from recovery_dek_salt
- legacy docs without the field fall back to dek_salt on read (their
  recovery KEK was derived from it), and change-password materializes
  that fallback before rotating the salt out from under it
- regression test drives register -> change-password -> reset-password
  through a stateful repo mock that actually persists credential updates

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
gertjan deleted branch claude/wonderful-perlman-083ccd 2026-07-10 11:51:39 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
gertjan/bodega!82
No description provided.