validate([ 'phone' => 'required', ]); $phone = preg_replace("/[^0-9]/", "", $request->phone); // Define the same rate limit key to share attempts between auth and resend $rateLimitKey = 'verify_attempts_' . $phone; // Set the same maximum attempts and decay time $maxAttempts = 5; $decayMinutes = 1; // Check if the phone number has exceeded the rate limit if (RateLimiter::tooManyAttempts($rateLimitKey, $maxAttempts)) { $seconds = RateLimiter::availableIn($rateLimitKey); return response()->json([ 'message' => 'Too many attempts. Please try again in ' . $seconds . ' seconds.' ], 429); // 429 Too Many Requests } $user = User::where('phone', $phone)->first(); if ($user) { // send SMS with verify code $verify_code = SmsService::send($phone); $user->update([ 'verify_code' => $verify_code, ]); // Increase the attempt count RateLimiter::hit($rateLimitKey, $decayMinutes * 60); return response()->json(['phone' => $phone]); } // return can't find user message return response()->json(['message' => 'User not found']); } public function auth(Request $request) { $phone = preg_replace("/[^0-9]/", "", $request->input('phone')); // Define the rate limit key based on the phone number. $rateLimitKey = 'verify_attempts_' . $phone; // Set the maximum attempts and the decay time (e.g., 5 attempts every 1 minute) $maxAttempts = 5; $decayMinutes = 1; // Check if the phone number has exceeded the rate limit if (RateLimiter::tooManyAttempts($rateLimitKey, $maxAttempts)) { $seconds = RateLimiter::availableIn($rateLimitKey); return response()->json([ 'message' => 'Too many attempts. Please try again in ' . $seconds . ' seconds.' ], 429); // 429 Too Many Requests } $user = User::where('phone', $phone)->first(); // send SMS with verify code $verify_code = SmsService::send($phone); if ($user) { $user->update([ 'verify_code' => $verify_code, ]); } else { User::create([ 'phone' => $phone, 'verify_code' => $verify_code, ]); } // Increase the attempt count RateLimiter::hit($rateLimitKey, $decayMinutes * 60); return response()->json(['phone' => $phone]); } public function verify(Request $request) { $request->validate([ 'phone' => 'required', 'verify_code' => 'required', ]); $phone = preg_replace("/[^0-9]/", "", $request->phone); $user = User::where('phone', $phone)->first(); // check if user exists if (!$user) { return response()->json([ 'message' => 'User not found' ], Response::HTTP_NOT_FOUND); } // check if verify code is correct if ($user->verify_code != $request->verify_code) { return response([ 'message' => 'Verify code is incorrect' ], Response::HTTP_UNAUTHORIZED); } $token = $user->createToken('token')->plainTextToken; $cookie = cookie('jwt', $token, 60 * 24 * 365, null, null, secure: true, httpOnly: true); // 1 year expiration // clear verify code $user->update([ 'verify_code' => null, ]); // Update cart, remove token and add user_id $this->updateCart($user); return response([ 'data' => [ 'id' => $user->id, 'phone' => $user->phone, 'access_token' => $token ] ])->withCookie($cookie); } public function logout() { $cookie = Cookie::forget('jwt'); // logout auth()->user()->tokens()->delete(); return response([ 'message' => 'Success' ])->withCookie($cookie); } private function getLocalToken() { return request()->header('X-Application-Token'); } private function updateCart($user) { if ($this->getLocalToken()) { // Update carts, remove token and add user_id $carts = Cart::where('token', $this->getLocalToken())->get(); foreach ($carts as $cart) { $cart->update([ 'token' => null, 'user_id' => $user->id, ]); } } } }