Repository navigation
fix(auth): harden MFA recovery codes and enrollment state - #686
Conversation
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 69 |
| Duplication | 0 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
There was a problem hiding this comment.
Copilot review overview
🟡 Changes recommended
Anonymous verification can trigger multiple expensive PBKDF2 derivations per request, creating a CPU-exhaustion path.
Review effort: Balanced
Findings: 1
Open (2)
What changed in this PR
Hardens MFA recovery-code storage, concurrency, enrollment expiry, API compatibility, and validation coverage while retaining legacy support.
Changes:
- Adds versioned salted PBKDF2 recovery codes and issuance metadata.
- Adds optimistic concurrency and fixed TOTP enrollment deadlines.
- Regenerates clients and expands unit/integration contract coverage.
| File | Description |
|---|---|
packages/infrastructure/client/tests/validation/mfa-recovery-contract.test.ts |
Tests additive status compatibility. |
packages/infrastructure/client/src/generated/types.gen.ts |
Adds recovery metadata types. |
packages/infrastructure/client/src/generated/.metadata.json |
Updates generation metadata. |
docs/architecture/mfa-recovery-reconciliation.md |
Records scope and evidence. |
apps/api/tests/GameGuild.SharedKernel.UnitTests/Configuration/ConfigurationOptionsTests.cs |
Updates default-length assertion. |
apps/api/tests/GameGuild.SharedKernel.UnitTests/Configuration/ConfigurationCoreOptionsTests.cs |
Updates core default assertion. |
apps/api/tests/GameGuild.Identity.Authentication.UnitTests/Services/TotpRecoverySecurityTests.cs |
Adds TOTP security tests. |
apps/api/tests/GameGuild.Identity.Authentication.UnitTests/Services/BackupCodeRecoverySecurityTests.cs |
Adds recovery-code tests. |
apps/api/tests/GameGuild.API.IntegrationTests/MfaRecoveryPostgreSqlTests.cs |
Tests PostgreSQL concurrency and HTTP contracts. |
apps/api/Source/Modules/GameGuild.SharedKernel/Configuration/ApplicationLayer/MfaOptions.cs |
Changes default code length. |
apps/api/Source/Modules/GameGuild.Identity.Authentication/Services/TotpMfaService.cs |
Persists enrollment state and expiry. |
apps/api/Source/Modules/GameGuild.Identity.Authentication/Services/MfaService.cs |
Propagates cancellation. |
apps/api/Source/Modules/GameGuild.Identity.Authentication/Services/MfaAttemptTrackingService.cs |
Retries concurrent failure updates. |
apps/api/Source/Modules/GameGuild.Identity.Authentication/Services/BackupCodeSet.cs |
Encodes issued-set metadata. |
apps/api/Source/Modules/GameGuild.Identity.Authentication/Services/BackupCodeMfaService.cs |
Implements hardened code handling. |
apps/api/Source/Modules/GameGuild.Identity.Authentication/Repositories/UserMfaConfigurationRepository.cs |
Reloads concurrency conflicts. |
apps/api/Source/Modules/GameGuild.Identity.Authentication/Entities/UserMfaConfiguration.cs |
Adds setup expiration state. |
apps/api/Source/Modules/GameGuild.Identity.Authentication/DTOs/MfaConfigurationResponse.cs |
Exposes issued count. |
apps/api/Source/Modules/GameGuild.Identity.Authentication/Data/Configurations/UserMfaConfigurationConfiguration.cs |
Configures concurrency tokens. |
apps/api/Source/Modules/GameGuild.Identity.Authentication/Controllers/MfaController.cs |
Preserves numeric status contracts. |
apps/api/Source/Modules/GameGuild.Identity.Authentication/AUTHORIZATION_ARCHITECTURE.md |
Documents recovery invariants. |
apps/api/Source/GameGuild.API/Database/Migrations/ApplicationDbContextModelSnapshot.cs |
Updates the EF model snapshot. |
apps/api/Source/GameGuild.API/Database/Migrations/20261005013448_MfaRecoveryConcurrency.cs |
Adds setup-expiry column. |
apps/api/Source/GameGuild.API/Database/Migrations/20261005013448_MfaRecoveryConcurrency.Designer.cs |
Defines migration target model. |
apps/api/Source/GameGuild.API/appsettings.json |
Configures 12-character codes. |
💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| foreach (var hashedCode in hashedCodes) | ||
| { | ||
| if (await VerifyBackupCodeHashAsync(backupCode, hashedCode, cancellationToken)) |
| if (bits >= 8) { bits -= 8; key.Add((byte)(buffer >> bits)); } | ||
| } | ||
| var counter = new byte[8]; | ||
| BinaryPrimitives.WriteInt64BigEndian(counter, DateTimeOffset.UtcNow.ToUnixTimeSeconds() / 30 + offset); |
|
Exact head Commit Fresh local Release API architecture/security/JWT selection passed 106/106. A preceding Debug repetition crashed after 75 cases and is retained/excluded, with no additional coverage claimed. #223/#243/#285 remain OPEN with their remaining functional acceptance; this PR does not close them automatically. |
MFA recovery increment merged — original issue remains OPENPR #686 is MERGED into develop at All applicable matching-head checks passed: API, Web, clean migration/model compatibility, OpenAPI/client consistency, repository policy, tracked-artifact checks, PR Required Gate, Codacy and four CodeQL analyses. CI run executed 151 API integration, 1,049 API unit, 2,083 authentication and 1,371 SharedKernel cases, all passing (4,654 principal executions), followed by 14 passing repeated OpenAPI HTTP cases. Both Release builds had zero warnings/errors. Nonapplicable Testing Lab/Economy/Emception jobs were skipped. Local evidence remains 5,239 distinct cases, including 35 new cases, with the full warning/error-clean solution build, real migrated PostgreSQL concurrency/replay/actor/lockout checks, legacy compatibility and regenerated client contracts. Repeated, superseded, interrupted and crashed attempts are excluded; the previous head's 20-minute API cancellation is retained as failure history. The API allowance was extended to 30 minutes without dropping a test or gate. Implementation and boundary map: full original issuance counts for new sets, explicit unknown legacy history, 12-character/600,000-iteration backup criteria, guarded concurrent single use, pending-enrollment rejection and fixed new-enrollment deadline/completion state. #223, #243 and #285 remain OPEN. Configured external recovery-email delivery and a real authenticator QR round trip remain unverified. #243 retains its native child. Existing anonymous MFA verification returns no access/refresh tokens; complete sign-in challenge/session acceptance is not certified by this increment. No automatic closure or production-delivery claim is made. The primary develop checkout is synchronized with all 55 unrelated local files preserved byte-for-byte and by status. The integrated branch was removed locally/remotely. Two checkouts are reused and no stashes remain; password hashing/policy work under #254/#255 continues in the isolated checkout. |
…codes-20261004 fix(auth): harden MFA recovery codes and enrollment state


Summary
Related to #223, #243 and #285. These issues remain open: configured provider email delivery and real authenticator QR acceptance are still required. This PR is an implementation increment, not a full issue-closure claim.
Local validation
Exact-head GitHub checks pending.