feat: add 2FA recovery codes regeneration and backup functionality, enhancing account security

This commit is contained in:
Usman Baig
2026-02-23 11:43:57 +01:00
parent b54af6c03a
commit 27b3aa8380
4 changed files with 11 additions and 6 deletions

View File

@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
## [Unreleased] ## [Unreleased]
### Added
- **2FA recovery codes backup.** When you enable 2FA, you receive recovery codes. You can now regenerate new codes (with password confirmation) from Settings and download them as a `.txt` file. Regenerating invalidates all existing codes.
### Fixed ### Fixed
- **2FA disable now requires password confirmation.** Disabling 2FA sends the derived password to the backend for verification. This prevents an attacker with a hijacked session from stripping 2FA. - **2FA disable now requires password confirmation.** Disabling 2FA sends the derived password to the backend for verification. This prevents an attacker with a hijacked session from stripping 2FA.

View File

@@ -34,8 +34,9 @@ export async function disable2FA(passwordDerived: string): Promise<void> {
}) })
} }
export async function regenerateRecoveryCodes(): Promise<RegenerateCodesResponse> { export async function regenerateRecoveryCodes(passwordDerived: string): Promise<RegenerateCodesResponse> {
return apiRequest<RegenerateCodesResponse>('/auth/2fa/recovery', { return apiRequest<RegenerateCodesResponse>('/auth/2fa/recovery', {
method: 'POST', method: 'POST',
body: JSON.stringify({ password: passwordDerived }),
}) })
} }

8
package-lock.json generated
View File

@@ -8,7 +8,7 @@
"name": "pulse-frontend", "name": "pulse-frontend",
"version": "0.11.1-alpha", "version": "0.11.1-alpha",
"dependencies": { "dependencies": {
"@ciphera-net/ui": "^0.0.59", "@ciphera-net/ui": "^0.0.60",
"@ducanh2912/next-pwa": "^10.2.9", "@ducanh2912/next-pwa": "^10.2.9",
"@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-icons": "^1.3.0",
"@stripe/react-stripe-js": "^5.6.0", "@stripe/react-stripe-js": "^5.6.0",
@@ -1541,9 +1541,9 @@
} }
}, },
"node_modules/@ciphera-net/ui": { "node_modules/@ciphera-net/ui": {
"version": "0.0.59", "version": "0.0.60",
"resolved": "https://npm.pkg.github.com/download/@ciphera-net/ui/0.0.59/220eabb8186f92af5f38d26f6a6515fd55f2650c", "resolved": "https://npm.pkg.github.com/download/@ciphera-net/ui/0.0.60/8d3b666ea855e202cf55fa6bdf7553c843635203",
"integrity": "sha512-HFjtTmeljbEroDJhkHV200cwVRW1qAzymBiwYErqF4J5W21GN+gfY4w31AHCjSsZgmNMOEprvqZp3ll2wwGcKg==", "integrity": "sha512-993Zsc4TGYrjO7cG4Q7oskgo0U+fEY4s8mDmR/jhdmZQv83bNXG9YgjvWcePhojhsVf+Nyo1DA2Nm0j/fwAzaA==",
"dependencies": { "dependencies": {
"@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-icons": "^1.3.0",
"clsx": "^2.1.0", "clsx": "^2.1.0",

View File

@@ -10,7 +10,7 @@
"type-check": "tsc --noEmit" "type-check": "tsc --noEmit"
}, },
"dependencies": { "dependencies": {
"@ciphera-net/ui": "^0.0.59", "@ciphera-net/ui": "^0.0.60",
"@ducanh2912/next-pwa": "^10.2.9", "@ducanh2912/next-pwa": "^10.2.9",
"@radix-ui/react-icons": "^1.3.0", "@radix-ui/react-icons": "^1.3.0",
"@stripe/react-stripe-js": "^5.6.0", "@stripe/react-stripe-js": "^5.6.0",