From d7d8ff8afd4ec94b7367e76bc4190f80fb1496c1 Mon Sep 17 00:00:00 2001 From: Flaminel Date: Sat, 22 Aug 2026 12:00:16 +0300 Subject: [PATCH] Fix 2FA code rejected when it has spaces (#743) --- .../Contracts/Requests/VerifyTotpRequest.cs | 2 +- .../Features/Auth/TotpServiceTests.cs | 18 +++++++++++++++ .../Features/Auth/TotpService.cs | 13 ++++++++--- .../features/auth/login/login.component.html | 4 ++-- .../auth/login/login.component.spec.ts | 18 +++++++++++++++ .../features/auth/login/login.component.ts | 4 ++-- .../features/auth/setup/setup.component.html | 2 +- .../auth/setup/setup.component.spec.ts | 18 +++++++++++++++ .../features/auth/setup/setup.component.ts | 2 +- .../account/two-factor-card.component.html | 2 +- .../account/two-factor-card.component.spec.ts | 14 +++++++++++ .../account/two-factor-card.component.ts | 2 +- e2e/tests/auth/two-factor.api.spec.ts | 23 +++++++++++++++++++ 13 files changed, 110 insertions(+), 12 deletions(-) diff --git a/code/backend/Cleanuparr.Api/Features/Auth/Contracts/Requests/VerifyTotpRequest.cs b/code/backend/Cleanuparr.Api/Features/Auth/Contracts/Requests/VerifyTotpRequest.cs index 590c3cd8..1047bccc 100644 --- a/code/backend/Cleanuparr.Api/Features/Auth/Contracts/Requests/VerifyTotpRequest.cs +++ b/code/backend/Cleanuparr.Api/Features/Auth/Contracts/Requests/VerifyTotpRequest.cs @@ -5,6 +5,6 @@ namespace Cleanuparr.Api.Features.Auth.Contracts.Requests; public sealed record VerifyTotpRequest { [Required] - [StringLength(6, MinimumLength = 6)] + [StringLength(32)] public required string Code { get; init; } } diff --git a/code/backend/Cleanuparr.Infrastructure.Tests/Features/Auth/TotpServiceTests.cs b/code/backend/Cleanuparr.Infrastructure.Tests/Features/Auth/TotpServiceTests.cs index ffc77908..75fc39e1 100644 --- a/code/backend/Cleanuparr.Infrastructure.Tests/Features/Auth/TotpServiceTests.cs +++ b/code/backend/Cleanuparr.Infrastructure.Tests/Features/Auth/TotpServiceTests.cs @@ -68,6 +68,24 @@ public sealed class TotpServiceTests _sut.VerifySecondFactor(user, codes[0].Replace("-", string.Empty).ToLowerInvariant()).ShouldBeTrue(); } + [Fact] + public void VerifySecondFactor_WithPaddedTotpCode_ReturnsTrue() + { + string secret = _sut.GenerateSecret(); + User user = CreateUser(secret); + + _sut.VerifySecondFactor(user, $" {ComputeTotp(secret)} ").ShouldBeTrue(); + } + + [Fact] + public void VerifySecondFactor_WithPaddedRecoveryCode_ReturnsTrue() + { + List codes = _sut.GenerateRecoveryCodes(); + User user = CreateUser(_sut.GenerateSecret(), codes); + + _sut.VerifySecondFactor(user, $" {codes[0]}\n").ShouldBeTrue(); + } + private User CreateUser(string secret, List? recoveryCodes = null) { return new User diff --git a/code/backend/Cleanuparr.Infrastructure/Features/Auth/TotpService.cs b/code/backend/Cleanuparr.Infrastructure/Features/Auth/TotpService.cs index ff86512a..58b18fbd 100644 --- a/code/backend/Cleanuparr.Infrastructure/Features/Auth/TotpService.cs +++ b/code/backend/Cleanuparr.Infrastructure/Features/Auth/TotpService.cs @@ -24,7 +24,14 @@ public sealed class TotpService : ITotpService /// public bool ValidateCode(string secret, string code) { - if (string.IsNullOrWhiteSpace(code) || code.Length != 6) + if (string.IsNullOrWhiteSpace(code)) + { + return false; + } + + string trimmedCode = code.Trim(); + + if (trimmedCode.Length != 6) { return false; } @@ -33,7 +40,7 @@ public sealed class TotpService : ITotpService { var keyBytes = Base32Encoding.ToBytes(secret); var totp = new Totp(keyBytes); - return totp.VerifyTotp(code, out _, new VerificationWindow(previous: 1, future: 1)); + return totp.VerifyTotp(trimmedCode, out _, new VerificationWindow(previous: 1, future: 1)); } catch { @@ -72,7 +79,7 @@ public sealed class TotpService : ITotpService { try { - var normalized = code.Replace("-", "").ToUpperInvariant(); + string normalized = code.Trim().Replace("-", "").ToUpperInvariant(); return BCrypt.Net.BCrypt.Verify(normalized, hash); } catch diff --git a/code/frontend/src/app/features/auth/login/login.component.html b/code/frontend/src/app/features/auth/login/login.component.html index 01cd7553..5679918e 100644 --- a/code/frontend/src/app/features/auth/login/login.component.html +++ b/code/frontend/src/app/features/auth/login/login.component.html @@ -102,7 +102,7 @@