feat: enhance OTP verification logic to support master code bypass and improve error handling

This commit is contained in:
masoodafar-web
2026-04-29 22:58:16 +03:30
parent 4a7df2083e
commit 04e1a83838
@@ -26,26 +26,27 @@ public class VerifyOtpTokenCommandHandler : IRequestHandler<VerifyOtpTokenComman
var purpose = request.Purpose?.ToLowerInvariant() ?? "login";
var now = DateTime.Now;
var otpToken = await _context.OtpTokens
.Where(x => x.Mobile == mobile && x.Purpose == purpose && !x.IsUsed && x.ExpiresAt > now)
.OrderByDescending(x => x.Id)
.FirstOrDefaultAsync(cancellationToken);
if (otpToken == null)
return new VerifyOtpTokenResponseDto { Success = false, Message = "کد تایید منقضی شده یا وجود ندارد. لطفاً کد جدید دریافت کنید." };
// بررسی تعداد تلاش
if (otpToken.Attempts >= MaxAttempts)
return new VerifyOtpTokenResponseDto { Success = false, Message = "تعداد تلاش‌ها زیاد است. لطفاً کد جدید دریافت کنید." };
// بررسی کد مستر (فقط برای لاگین)
// ── بررسی کد مستر (فقط برای لاگین، بدون نیاز به OTP در DB) ──
var masterCode = _cfg["Otp:MasterCode"];
var isMasterCode = purpose == "login"
&& !string.IsNullOrWhiteSpace(masterCode)
&& request.Code == masterCode;
OtpToken? otpToken = null;
if (!isMasterCode)
{
otpToken = await _context.OtpTokens
.Where(x => x.Mobile == mobile && x.Purpose == purpose && !x.IsUsed && x.ExpiresAt > now)
.OrderByDescending(x => x.Id)
.FirstOrDefaultAsync(cancellationToken);
if (otpToken == null)
return new VerifyOtpTokenResponseDto { Success = false, Message = "کد تایید منقضی شده یا وجود ندارد. لطفاً کد جدید دریافت کنید." };
// بررسی تعداد تلاش
if (otpToken.Attempts >= MaxAttempts)
return new VerifyOtpTokenResponseDto { Success = false, Message = "تعداد تلاش‌ها زیاد است. لطفاً کد جدید دریافت کنید." };
otpToken.Attempts++;
// Verify using HMAC-SHA256
@@ -73,9 +74,13 @@ public class VerifyOtpTokenCommandHandler : IRequestHandler<VerifyOtpTokenComman
.Where(x => x.Mobile == mobile)
.FirstOrDefaultAsync(cancellationToken);
// ── کاربر وجود ندارد → ثبت‌نام جدید ──
// ── کاربر وجود ندارد → ثبت‌نام جدید (فقط برای OTP عادی) ──
if (user == null)
{
// با کد مستر، ثبت‌نام جدید مجاز نیست
if (isMasterCode)
return new VerifyOtpTokenResponseDto { Success = false, Message = "کاربری با این شماره موبایل وجود ندارد." };
// کد معرف الزامی است
if (string.IsNullOrWhiteSpace(request.ParentReferralCode))
return new VerifyOtpTokenResponseDto { Success = false, Message = "کد معرف الزامی است." };
@@ -156,8 +161,11 @@ public class VerifyOtpTokenCommandHandler : IRequestHandler<VerifyOtpTokenComman
user.MobileVerifiedAt ??= now;
}
// Mark OTP as used
otpToken.IsUsed = true;
// Mark OTP as used (only for non-master-code flows)
if (otpToken != null)
{
otpToken.IsUsed = true;
}
await _context.SaveChangesAsync(cancellationToken);
// Generate JWT token