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": {
"Enabled": true,
"Provider": "Kavenegar",
"KavenegarApiKey": "497263626F32626A48685A6137524C4F78575A766E4C74694A556B79317648424964655030682B554545413D",
"KavenegarApiKey": "43676A4E786A6C50452F2F6252507939346A5145764B33566B456454374E657A614468706658656D6534593D",
"Sender": "1000001110100"
},
"DayaPayment": {
@@ -55,6 +55,9 @@ public class VerifyOtpTokenCommandHandler : IRequestHandler<VerifyOtpTokenComman
if (user is null)
{
if (purpose == "login")
return new VerifyOtpTokenResponseDto() { Success = false, Message = "کاربری با این شماره موبایل وجود ندارد." };
if (request.ParentReferralCode == null)
return new VerifyOtpTokenResponseDto() { Success = false, Message = "کد معرف الزامی است." };
@@ -26,32 +26,42 @@ 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);
// ── بررسی کد مستر (فقط برای لاگین، بدون نیاز به OTP در DB) ──
var masterCode = _cfg["Otp:MasterCode"];
var isMasterCode = purpose == "login"
&& !string.IsNullOrWhiteSpace(masterCode)
&& request.Code == masterCode;
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))
OtpToken? otpToken = null;
if (!isMasterCode)
{
await _context.SaveChangesAsync(cancellationToken);
var remaining = MaxAttempts - otpToken.Attempts;
return new VerifyOtpTokenResponseDto
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
var secret = _cfg["Otp:Secret"] ?? throw new InvalidOperationException("Otp:Secret not set");
if (!_hashService.VerifyHmacSha256Hex(request.Code, otpToken.CodeHash, secret))
{
Success = false,
Message = "کد تایید نادرست است.",
RemainingAttempts = remaining
};
await _context.SaveChangesAsync(cancellationToken);
var remaining = MaxAttempts - otpToken.Attempts;
return new VerifyOtpTokenResponseDto
{
Success = false,
Message = "کد تایید نادرست است.",
RemainingAttempts = remaining
};
}
}
// ── جستجوی کاربر ──
@@ -64,12 +74,16 @@ public class VerifyOtpTokenCommandHandler : IRequestHandler<VerifyOtpTokenComman
.Where(x => x.Mobile == mobile)
.FirstOrDefaultAsync(cancellationToken);
// ── کاربر وجود ندارد → ثبت‌نام جدید ──
// ── کاربر وجود ندارد → ثبت‌نام جدید (فقط برای OTP عادی) ──
if (user == null)
{
// کد معرف الزامی است
// با کد مستر، ثبت‌نام جدید مجاز نیست
if (isMasterCode)
return new VerifyOtpTokenResponseDto { Success = false, Message = "کاربری با این شماره موبایل وجود ندارد." };
// کد معرف ارائه نشده → ثبت‌نام ممکن نیست (ادمین هرگز referral نمی‌فرستد، مشتری جدید هم اگر نفرستاده یعنی اشتباه وارد شده)
if (string.IsNullOrWhiteSpace(request.ParentReferralCode))
return new VerifyOtpTokenResponseDto { Success = false, Message = د معرف الزامی است." };
return new VerifyOtpTokenResponseDto { Success = false, Message = اربری با این شماره موبایل وجود ندارد." };
// بررسی وجود معرف و فعال بودن عضویت باشگاه
var parent = await _context.Users
@@ -147,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
@@ -15,7 +15,8 @@
"providerName": "System.Data.SqlClient"
},
"Otp": {
"Secret": "K2w8k1h1mH2Qz1kqWk0c8kQ2Pq8q9H1eE2nqN1qQ8x7M="
"Secret": "K2w8k1h1mH2Qz1kqWk0c8kQ2Pq8q9H1eE2nqN1qQ8x7M=",
"MasterCode": "1122334455"
},
"Monitoring": {
"SentryEnabled": false,