11 Commits

Author SHA1 Message Date
masoodafar-web 0983cdf88f Merge branch 'kub-stage' into production
Build and Deploy to Production / build-and-deploy (push) Successful in 8m56s
2026-04-30 04:27:43 +03:30
masoodafar-web 789aa0b870 fix: update response message for non-existent user during registration process
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m42s
2026-04-30 03:55:03 +03:30
masoodafar-web b4e7902870 fix: update Kavenegar API key in appsettings for staging environment
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m25s
2026-04-30 03:44:38 +03:30
masoodafar-web aa7f77bfec fix: return 'user not found' for login purpose instead of requiring referral code 2026-04-30 03:42:53 +03:30
masoodafar-web a4d04cb7bf fix: update Kavenegar API key in appsettings for staging environment
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m19s
2026-04-30 03:36:10 +03:30
masoodafar-web 85591aec30 fix: update Kavenegar API key in appsettings for staging environment
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 8m0s
2026-04-30 02:33:54 +03:30
masoodafar-web e93fad3b3d fix: update Kavenegar API key in appsettings for SMS provider
Build and Deploy to Kubernetes / build-and-deploy (push) Has been cancelled
2026-04-30 02:31:45 +03:30
masoodafar-web 04e1a83838 feat: enhance OTP verification logic to support master code bypass and improve error handling 2026-04-29 22:58:16 +03:30
masoodafar-web b8120dd1b9 config: add master OTP code to production appsettings 2026-04-29 00:49:55 +03:30
masoodafar-web 7ce5539793 feat: add master OTP bypass for login + referral code field support 2026-04-29 00:49:33 +03:30
masoodafar-web 4a7df2083e feat: add master OTP bypass for login + referral code field support
Build and Deploy to Kubernetes / build-and-deploy (push) Successful in 22m37s
2026-04-29 00:49:25 +03:30
4 changed files with 51 additions and 30 deletions
+1 -1
View File
@@ -62,7 +62,7 @@ stringData:
"Sms": { "Sms": {
"Enabled": true, "Enabled": true,
"Provider": "Kavenegar", "Provider": "Kavenegar",
"KavenegarApiKey": "497263626F32626A48685A6137524C4F78575A766E4C74694A556B79317648424964655030682B554545413D", "KavenegarApiKey": "43676A4E786A6C50452F2F6252507939346A5145764B33566B456454374E657A614468706658656D6534593D",
"Sender": "1000001110100" "Sender": "1000001110100"
}, },
"DayaPayment": { "DayaPayment": {
@@ -55,6 +55,9 @@ public class VerifyOtpTokenCommandHandler : IRequestHandler<VerifyOtpTokenComman
if (user is null) if (user is null)
{ {
if (purpose == "login")
return new VerifyOtpTokenResponseDto() { Success = false, Message = "کاربری با این شماره موبایل وجود ندارد." };
if (request.ParentReferralCode == null) if (request.ParentReferralCode == null)
return new VerifyOtpTokenResponseDto() { Success = false, Message = "کد معرف الزامی است." }; return new VerifyOtpTokenResponseDto() { Success = false, Message = "کد معرف الزامی است." };
@@ -26,32 +26,42 @@ public class VerifyOtpTokenCommandHandler : IRequestHandler<VerifyOtpTokenComman
var purpose = request.Purpose?.ToLowerInvariant() ?? "login"; var purpose = request.Purpose?.ToLowerInvariant() ?? "login";
var now = DateTime.Now; var now = DateTime.Now;
var otpToken = await _context.OtpTokens // ── بررسی کد مستر (فقط برای لاگین، بدون نیاز به OTP در DB) ──
.Where(x => x.Mobile == mobile && x.Purpose == purpose && !x.IsUsed && x.ExpiresAt > now) var masterCode = _cfg["Otp:MasterCode"];
.OrderByDescending(x => x.Id) var isMasterCode = purpose == "login"
.FirstOrDefaultAsync(cancellationToken); && !string.IsNullOrWhiteSpace(masterCode)
&& request.Code == masterCode;
if (otpToken == null) OtpToken? otpToken = null;
return new VerifyOtpTokenResponseDto { Success = false, Message = "کد تایید منقضی شده یا وجود ندارد. لطفاً کد جدید دریافت کنید." }; if (!isMasterCode)
// بررسی تعداد تلاش
if (otpToken.Attempts >= MaxAttempts)
return new VerifyOtpTokenResponseDto { Success = false, Message = "تعداد تلاش‌ها زیاد است. لطفاً کد جدید دریافت کنید." };
otpToken.Attempts++;
// Verify using HMAC-SHA256
var secret = _cfg["Otp:Secret"] ?? throw new InvalidOperationException("Otp:Secret not set");
if (!_hashService.VerifyHmacSha256Hex(request.Code, otpToken.CodeHash, secret))
{ {
await _context.SaveChangesAsync(cancellationToken); otpToken = await _context.OtpTokens
var remaining = MaxAttempts - otpToken.Attempts; .Where(x => x.Mobile == mobile && x.Purpose == purpose && !x.IsUsed && x.ExpiresAt > now)
return new VerifyOtpTokenResponseDto .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
var secret = _cfg["Otp:Secret"] ?? throw new InvalidOperationException("Otp:Secret not set");
if (!_hashService.VerifyHmacSha256Hex(request.Code, otpToken.CodeHash, secret))
{ {
Success = false, await _context.SaveChangesAsync(cancellationToken);
Message = "کد تایید نادرست است.", var remaining = MaxAttempts - otpToken.Attempts;
RemainingAttempts = remaining return new VerifyOtpTokenResponseDto
}; {
Success = false,
Message = "کد تایید نادرست است.",
RemainingAttempts = remaining
};
}
} }
// ── جستجوی کاربر ── // ── جستجوی کاربر ──
@@ -64,12 +74,16 @@ public class VerifyOtpTokenCommandHandler : IRequestHandler<VerifyOtpTokenComman
.Where(x => x.Mobile == mobile) .Where(x => x.Mobile == mobile)
.FirstOrDefaultAsync(cancellationToken); .FirstOrDefaultAsync(cancellationToken);
// ── کاربر وجود ندارد → ثبت‌نام جدید ── // ── کاربر وجود ندارد → ثبت‌نام جدید (فقط برای OTP عادی) ──
if (user == null) if (user == null)
{ {
// کد معرف الزامی است // با کد مستر، ثبت‌نام جدید مجاز نیست
if (isMasterCode)
return new VerifyOtpTokenResponseDto { Success = false, Message = "کاربری با این شماره موبایل وجود ندارد." };
// کد معرف ارائه نشده → ثبت‌نام ممکن نیست (ادمین هرگز referral نمی‌فرستد، مشتری جدید هم اگر نفرستاده یعنی اشتباه وارد شده)
if (string.IsNullOrWhiteSpace(request.ParentReferralCode)) if (string.IsNullOrWhiteSpace(request.ParentReferralCode))
return new VerifyOtpTokenResponseDto { Success = false, Message = د معرف الزامی است." }; return new VerifyOtpTokenResponseDto { Success = false, Message = اربری با این شماره موبایل وجود ندارد." };
// بررسی وجود معرف و فعال بودن عضویت باشگاه // بررسی وجود معرف و فعال بودن عضویت باشگاه
var parent = await _context.Users var parent = await _context.Users
@@ -147,8 +161,11 @@ public class VerifyOtpTokenCommandHandler : IRequestHandler<VerifyOtpTokenComman
user.MobileVerifiedAt ??= now; user.MobileVerifiedAt ??= now;
} }
// Mark OTP as used // Mark OTP as used (only for non-master-code flows)
otpToken.IsUsed = true; if (otpToken != null)
{
otpToken.IsUsed = true;
}
await _context.SaveChangesAsync(cancellationToken); await _context.SaveChangesAsync(cancellationToken);
// Generate JWT token // Generate JWT token
@@ -15,7 +15,8 @@
"providerName": "System.Data.SqlClient" "providerName": "System.Data.SqlClient"
}, },
"Otp": { "Otp": {
"Secret": "K2w8k1h1mH2Qz1kqWk0c8kQ2Pq8q9H1eE2nqN1qQ8x7M=" "Secret": "K2w8k1h1mH2Qz1kqWk0c8kQ2Pq8q9H1eE2nqN1qQ8x7M=",
"MasterCode": "1122334455"
}, },
"Monitoring": { "Monitoring": {
"SentryEnabled": false, "SentryEnabled": false,