Commit 25cefe7c authored by Mahmoud Aglan's avatar Mahmoud Aglan

OTP: switch to 4-digit codes, add 0000 universal bypass in demo mode

- Changed OTP from 6 digits to 4 digits
- Code 0000 always works in demo mode (no external SMS needed)
- Relaxed rate limit from 3 to 5 attempts, timeout 300s
- Validation accepts 4-6 digit codes for flexibility
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent 391629ac
......@@ -23,7 +23,7 @@ public function requestOtp(Request $request): JsonResponse
$phone = $this->normalizePhone($request->phone);
$rateLimitKey = "otp_request:{$phone}";
if (RateLimiter::tooManyAttempts($rateLimitKey, 3)) {
if (RateLimiter::tooManyAttempts($rateLimitKey, 5)) {
$seconds = RateLimiter::availableIn($rateLimitKey);
return response()->json([
'error' => 'too_many_attempts',
......@@ -38,7 +38,7 @@ public function requestOtp(Request $request): JsonResponse
->first();
if (!$user) {
RateLimiter::hit($rateLimitKey, 600);
RateLimiter::hit($rateLimitKey, 300);
return response()->json([
'error' => 'phone_not_found',
'message' => 'هذا الرقم غير مسجل في النظام',
......@@ -48,14 +48,13 @@ public function requestOtp(Request $request): JsonResponse
$mode = SystemSetting::get('auth_otp_mode', 'demo');
if ($mode === 'demo') {
Cache::put("otp:{$phone}", '123456', 300);
Cache::put("otp:{$phone}", '1234', 300);
} else {
$otp = str_pad(random_int(0, 999999), 6, '0', STR_PAD_LEFT);
$otp = str_pad(random_int(0, 9999), 4, '0', STR_PAD_LEFT);
Cache::put("otp:{$phone}", $otp, 300);
// TODO: Send SMS via SmsService when SMS mode is enabled
}
RateLimiter::hit($rateLimitKey, 600);
RateLimiter::hit($rateLimitKey, 300);
return response()->json([
'sent' => true,
......@@ -68,17 +67,23 @@ public function verify(Request $request): JsonResponse
{
$request->validate([
'phone' => 'required|string|min:10|max:15',
'otp' => 'required|string|size:6',
'otp' => 'required|string|min:4|max:6',
]);
$phone = $this->normalizePhone($request->phone);
$cached = Cache::get("otp:{$phone}");
if (!$cached || $cached !== $request->otp) {
return response()->json([
'error' => 'invalid_otp',
'message' => 'رمز التحقق غير صحيح',
], 401);
// Universal bypass: 0000 always works in demo mode
$mode = SystemSetting::get('auth_otp_mode', 'demo');
$isBypass = $mode === 'demo' && $request->otp === '0000';
if (!$isBypass) {
$cached = Cache::get("otp:{$phone}");
if (!$cached || $cached !== $request->otp) {
return response()->json([
'error' => 'invalid_otp',
'message' => 'رمز التحقق غير صحيح',
], 401);
}
}
Cache::forget("otp:{$phone}");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment