[PR #7231] fix(two-factor): updated backup codes respect storeBackupCodes option #7162

Open
opened 2026-03-13 13:26:15 -05:00 by GiteaMirror · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/better-auth/better-auth/pull/7231
Author: @Byte-Biscuit
Created: 1/9/2026
Status: 🔄 Open

Base: canary ← Head: fix/two-factor-backup-codes-update


📝 Commits (2)

  • 4a528b3 fix(two-factor): updated backup codes respect storeBackupCodes option
  • 991d760 Merge branch 'canary' into fix/two-factor-backup-codes-update

📊 Changes

2 files changed (+308 additions, -5 deletions)

View changed files

📝 packages/better-auth/src/plugins/two-factor/backup-codes/index.ts (+17 -4)
📝 packages/better-auth/src/plugins/two-factor/two-factor.test.ts (+291 -1)

📄 Description

🚀 Summary
This PR fixes a bug where backup codes became unusable after the first verification. The remaining backup codes were being saved using a hardcoded encryption strategy, failing to respect the storeBackupCodes configuration defined in the plugin options.

📋 Description
Prior to this fix, the verifyBackupCode logic in the two-factor plugin would always apply symmetricEncrypt to the updated backup code list before saving it to the database.

If a user configured storeBackupCodes: "plain" or provided a custom encryption object, the initial generation worked correctly, but the first successful verification would "corrupt" the storage format by forcing the default encryption. Subsequent verification attempts would fail to parse the stored data, effectively locking the user out of their remaining backup codes.

🛠 Changes
Updated backup-codes/index.ts: Refactored the update logic to dynamically choose the storage strategy (plain, default encryption, or custom encryption) based on the storeBackupCodes option.
Improved Consistency: Ensured that the data lifecycle (Generate -> Store -> Verify -> Re-store) maintains the same encryption/serialization policy.
🧪 Test Plan
Added comprehensive integration tests in two-factor.test.ts to verify the fix across different configurations:

  1. Plain Storage: Verified that codes remain as plain JSON strings after multiple uses.
  2. Encrypted Storage: Verified that codes remain encrypted using the internal symmetricEncrypt.
  3. Custom Storage: Verified that a custom encrypt/decrypt pair is respected during the update process.

All tests passed locally, ensuring that the remaining backup codes are correctly synchronized with the configured policy.


Summary by cubic

Fixes backup code updates to respect the storeBackupCodes option, preventing codes from breaking after the first use. Remaining codes are now saved with the same storage strategy (plain, encrypted, or custom) configured by the user.

  • Bug Fixes
    • Dynamically choose storage based on storeBackupCodes (plain, internal encryption, or custom encrypt/decrypt).
    • Preserve consistent format across generate → verify → re-store.
    • Added integration tests for plain, encrypted, and custom configurations.

Written for commit 991d760bf970c92a32bfd2e885c68ff950287257. Summary will update on new commits.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/better-auth/better-auth/pull/7231 **Author:** [@Byte-Biscuit](https://github.com/Byte-Biscuit) **Created:** 1/9/2026 **Status:** 🔄 Open **Base:** `canary` ← **Head:** `fix/two-factor-backup-codes-update` --- ### 📝 Commits (2) - [`4a528b3`](https://github.com/better-auth/better-auth/commit/4a528b3a65f0975d1999433a70f9b9e6ee1be20f) fix(two-factor): updated backup codes respect storeBackupCodes option - [`991d760`](https://github.com/better-auth/better-auth/commit/991d760bf970c92a32bfd2e885c68ff950287257) Merge branch 'canary' into fix/two-factor-backup-codes-update ### 📊 Changes **2 files changed** (+308 additions, -5 deletions) <details> <summary>View changed files</summary> 📝 `packages/better-auth/src/plugins/two-factor/backup-codes/index.ts` (+17 -4) 📝 `packages/better-auth/src/plugins/two-factor/two-factor.test.ts` (+291 -1) </details> ### 📄 Description 🚀 Summary This PR fixes a bug where backup codes became unusable after the first verification. The remaining backup codes were being saved using a hardcoded encryption strategy, failing to respect the storeBackupCodes configuration defined in the plugin options. 📋 Description Prior to this fix, the verifyBackupCode logic in the two-factor plugin would always apply symmetricEncrypt to the updated backup code list before saving it to the database. If a user configured storeBackupCodes: "plain" or provided a custom encryption object, the initial generation worked correctly, but the first successful verification would "corrupt" the storage format by forcing the default encryption. Subsequent verification attempts would fail to parse the stored data, effectively locking the user out of their remaining backup codes. 🛠 Changes Updated backup-codes/index.ts: Refactored the update logic to dynamically choose the storage strategy (plain, default encryption, or custom encryption) based on the storeBackupCodes option. Improved Consistency: Ensured that the data lifecycle (Generate -> Store -> Verify -> Re-store) maintains the same encryption/serialization policy. 🧪 Test Plan Added comprehensive integration tests in two-factor.test.ts to verify the fix across different configurations: 1. Plain Storage: Verified that codes remain as plain JSON strings after multiple uses. 2. Encrypted Storage: Verified that codes remain encrypted using the internal symmetricEncrypt. 3. Custom Storage: Verified that a custom encrypt/decrypt pair is respected during the update process. All tests passed locally, ensuring that the remaining backup codes are correctly synchronized with the configured policy. <!-- This is an auto-generated description by cubic. --> --- ## Summary by cubic Fixes backup code updates to respect the storeBackupCodes option, preventing codes from breaking after the first use. Remaining codes are now saved with the same storage strategy (plain, encrypted, or custom) configured by the user. - **Bug Fixes** - Dynamically choose storage based on storeBackupCodes (plain, internal encryption, or custom encrypt/decrypt). - Preserve consistent format across generate → verify → re-store. - Added integration tests for plain, encrypted, and custom configurations. <sup>Written for commit 991d760bf970c92a32bfd2e885c68ff950287257. Summary will update on new commits.</sup> <!-- End of auto-generated description by cubic. --> --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
GiteaMirror added the pull-request label 2026-03-13 13:26:15 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#7162