encryption = $encryption; } // ══════════════════════════════════════════════ // PASSENGER LOGIN // ══════════════════════════════════════════════ /** * POST /v2/auth/passenger/login * Replaces: login.php */ public function passengerLogin(Request $request): JsonResponse { $request->validate([ 'phone' => 'required|string', 'password' => 'required|string', 'fingerprint' => 'required|string', 'fcm_token' => 'required|string', ]); // Rate limiting: 5 attempts per minute per IP $rateLimitKey = 'login_passenger:' . $request->ip(); if (Cache::get($rateLimitKey, 0) >= config('intaleq.rate_limit_login', 5)) { return $this->failure('Too many login attempts. Please try again later.', 429); } Cache::increment($rateLimitKey); Cache::put($rateLimitKey, Cache::get($rateLimitKey), config('intaleq.rate_limit_login_decay', 60)); $phone = $request->input('phone'); $password = $request->input('password'); $fingerprint = $request->input('fingerprint'); $fcmToken = $request->input('fcm_token'); // Find passenger by encrypted phone $encryptedPhone = $this->encryption->encrypt($phone); $passenger = Passenger::active() ->where('phone', $encryptedPhone) ->first(); if (!$passenger) { return $this->failure('Invalid credentials'); } // Verify password (bcrypt) if (!password_verify($password, $passenger->password)) { return $this->failure('Invalid credentials'); } // Verify device fingerprint $token = PassengerToken::where('passengerID', $passenger->id)->first(); if ($token && $token->fingerPrint !== $fingerprint) { return $this->failure('Device mismatch. Please login from your registered device.'); } // Update FCM token if ($token) { $encryptedFcm = $this->encryption->encrypt($fcmToken); $token->update(['token' => $encryptedFcm]); } // Generate API keys if not exist if (empty($passenger->api_key)) { $this->generateApiKeys($passenger); } // Generate JWT $jwt = $this->createJwt($passenger->id, 'passenger', $fingerprint, 86400); // 24h return $this->success([ 'token' => $jwt, 'expires_in' => 86400, 'user_id' => $passenger->id, 'api_key' => $passenger->api_key, 'api_secret' => $passenger->api_secret, ]); } /** * POST /v2/auth/passenger/register * Replaces: loginFirstTime.php */ public function passengerRegister(Request $request): JsonResponse { $request->validate([ 'phone' => 'required|string', 'email' => 'required|email', 'password' => 'required|string|min:6', 'first_name' => 'required|string', 'last_name' => 'required|string', 'gender' => 'required|string', 'birthdate' => 'required|string', 'site' => 'required|string', 'fingerprint' => 'required|string', 'fcm_token' => 'required|string', ]); $phone = $request->input('phone'); $encryptedPhone = $this->encryption->encrypt($phone); // Check if already exists $exists = Passenger::where('phone', $encryptedPhone)->exists(); if ($exists) { return $this->failure('Phone number already registered'); } $passengerId = Str::uuid()->toString(); // Encrypt sensitive fields $passenger = Passenger::create([ 'id' => $passengerId, 'phone' => $encryptedPhone, 'email' => $this->encryption->encrypt($request->input('email')), 'password' => password_hash($request->input('password'), PASSWORD_BCRYPT), 'first_name' => $this->encryption->encrypt($request->input('first_name')), 'last_name' => $this->encryption->encrypt($request->input('last_name')), 'gender' => $this->encryption->encrypt($request->input('gender')), 'birthdate' => $this->encryption->encrypt($request->input('birthdate')), 'site' => $request->input('site'), ]); // Create FCM token record PassengerToken::create([ 'token' => $this->encryption->encrypt($request->input('fcm_token')), 'passengerID' => $passengerId, 'fingerPrint' => $request->input('fingerprint'), ]); // Generate API keys $this->generateApiKeys($passenger); // Generate temporary JWT (5 min — for registration flow) $jwt = $this->createJwt($passengerId, 'passenger_temp', $request->input('fingerprint'), 300); return $this->success([ 'token' => $jwt, 'expires_in' => 300, 'user_id' => $passengerId, 'api_key' => $passenger->api_key, 'api_secret' => $passenger->api_secret, ], 201); } // ══════════════════════════════════════════════ // DRIVER LOGIN // ══════════════════════════════════════════════ /** * POST /v2/auth/driver/login * Replaces: loginJwtDriver.php */ public function driverLogin(Request $request): JsonResponse { $request->validate([ 'phone' => 'required|string', 'password' => 'required|string', 'fingerprint' => 'required|string', 'fcm_token' => 'required|string', ]); // Rate limiting $rateLimitKey = 'login_driver:' . $request->ip(); if (Cache::get($rateLimitKey, 0) >= config('intaleq.rate_limit_login', 5)) { return $this->failure('Too many login attempts', 429); } Cache::increment($rateLimitKey); Cache::put($rateLimitKey, Cache::get($rateLimitKey), config('intaleq.rate_limit_login_decay', 60)); $phone = $request->input('phone'); $encryptedPhone = $this->encryption->encrypt($phone); $driver = Driver::active() ->where('phone', $encryptedPhone) ->first(); if (!$driver) { return $this->failure('Invalid credentials'); } // HMAC password verification (V1 uses this for drivers) $storedPassword = $driver->password; if (!password_verify($request->input('password'), $storedPassword) && !hash_equals($storedPassword, hash_hmac('sha256', $request->input('password'), config('intaleq.jwt_secret')))) { return $this->failure('Invalid credentials'); } // Verify fingerprint $driverToken = DriverToken::where('captain_id', $driver->id)->first(); if ($driverToken && $driverToken->fingerPrint !== $request->input('fingerprint')) { return $this->failure('Device mismatch'); } // Update FCM token $encryptedFcm = $this->encryption->encrypt($request->input('fcm_token')); if ($driverToken) { $driverToken->update([ 'token' => $encryptedFcm, 'fingerPrint' => $request->input('fingerprint'), ]); } else { DriverToken::create([ 'token' => $encryptedFcm, 'captain_id' => $driver->id, 'fingerPrint' => $request->input('fingerprint'), ]); } // Generate API keys if not exist if (empty($driver->api_key)) { $this->generateApiKeys($driver); } $jwt = $this->createJwt($driver->id, 'driver', $request->input('fingerprint'), 86400); return $this->success([ 'token' => $jwt, 'expires_in' => 86400, 'user_id' => $driver->id, 'api_key' => $driver->api_key, 'api_secret' => $driver->api_secret, ]); } /** * POST /v2/auth/driver/register * Replaces: loginFirstTimeDriver.php */ public function driverRegister(Request $request): JsonResponse { $request->validate([ 'phone' => 'required|string', 'email' => 'required|email', 'password' => 'required|string|min:6', 'first_name' => 'required|string', 'last_name' => 'required|string', 'gender' => 'required|string', 'birthdate' => 'required|string', 'site' => 'required|string', 'fingerprint' => 'required|string', 'fcm_token' => 'required|string', ]); $encryptedPhone = $this->encryption->encrypt($request->input('phone')); if (Driver::where('phone', $encryptedPhone)->exists()) { return $this->failure('Phone number already registered'); } $driverId = Str::uuid()->toString(); $driver = Driver::create([ 'id' => $driverId, 'phone' => $encryptedPhone, 'email' => $this->encryption->encrypt($request->input('email')), 'password' => password_hash($request->input('password'), PASSWORD_BCRYPT), 'first_name' => $this->encryption->encrypt($request->input('first_name')), 'last_name' => $this->encryption->encrypt($request->input('last_name')), 'gender' => $this->encryption->encrypt($request->input('gender')), 'birthdate' => $this->encryption->encrypt($request->input('birthdate')), 'site' => $request->input('site'), ]); DriverToken::create([ 'token' => $this->encryption->encrypt($request->input('fcm_token')), 'captain_id' => $driverId, 'fingerPrint' => $request->input('fingerprint'), ]); $this->generateApiKeys($driver); $jwt = $this->createJwt($driverId, 'driver_temp', $request->input('fingerprint'), 300); return $this->success([ 'token' => $jwt, 'expires_in' => 300, 'user_id' => $driverId, 'api_key' => $driver->api_key, 'api_secret' => $driver->api_secret, ], 201); } // ══════════════════════════════════════════════ // WALLET LOGIN (Higher security) // ══════════════════════════════════════════════ /** * POST /v2/auth/passenger/wallet-login * Replaces: loginWallet.php */ public function passengerWalletLogin(Request $request): JsonResponse { $request->validate([ 'phone' => 'required|string', 'password' => 'required|string', 'fingerprint' => 'required|string', ]); // Stricter rate limit for wallet $rateLimitKey = 'wallet_login:' . $request->ip(); if (Cache::get($rateLimitKey, 0) >= 3) { return $this->failure('Too many attempts', 429); } Cache::increment($rateLimitKey); Cache::put($rateLimitKey, Cache::get($rateLimitKey), 120); $encryptedPhone = $this->encryption->encrypt($request->input('phone')); $passenger = Passenger::active()->where('phone', $encryptedPhone)->first(); if (!$passenger || !password_verify($request->input('password'), $passenger->password)) { return $this->failure('Invalid credentials'); } // Short-lived token for wallet operations (5 min) $jwt = $this->createJwt($passenger->id, 'passenger_wallet', $request->input('fingerprint'), 300); return $this->success([ 'token' => $jwt, 'expires_in' => 300, 'user_id' => $passenger->id, ]); } /** * POST /v2/auth/driver/wallet-login * Replaces: loginJwtWalletDriver.php */ public function driverWalletLogin(Request $request): JsonResponse { $request->validate([ 'phone' => 'required|string', 'password' => 'required|string', 'fingerprint' => 'required|string', ]); $rateLimitKey = 'wallet_login_driver:' . $request->ip(); if (Cache::get($rateLimitKey, 0) >= 3) { return $this->failure('Too many attempts', 429); } Cache::increment($rateLimitKey); Cache::put($rateLimitKey, Cache::get($rateLimitKey), 120); $encryptedPhone = $this->encryption->encrypt($request->input('phone')); $driver = Driver::active()->where('phone', $encryptedPhone)->first(); if (!$driver || !password_verify($request->input('password'), $driver->password)) { return $this->failure('Invalid credentials'); } $jwt = $this->createJwt($driver->id, 'driver_wallet', $request->input('fingerprint'), 60); return $this->success([ 'token' => $jwt, 'expires_in' => 60, 'user_id' => $driver->id, ]); } // ══════════════════════════════════════════════ // ADMIN LOGIN // ══════════════════════════════════════════════ /** * POST /v2/auth/admin/login * Replaces: loginAdmin.php (NOW WITH ACTUAL PASSWORD CHECK!) */ public function adminLogin(Request $request): JsonResponse { $request->validate([ 'device_number' => 'required|string', 'password' => 'required|string', ]); $admin = DB::connection('primary') ->table('adminUser') ->where('device_number', $request->input('device_number')) ->first(); if (!$admin) { return $this->failure('Invalid credentials'); } // TODO: Add password field to adminUser table and verify with password_verify // For now, this is a placeholder — must be implemented before production $jwt = $this->createJwt($admin->id, 'admin', $request->input('device_number'), 900); return $this->success([ 'token' => $jwt, 'expires_in' => 900, 'user_id' => $admin->id, ]); } // ══════════════════════════════════════════════ // HELPERS // ══════════════════════════════════════════════ private function createJwt(string $userId, string $userType, string $fingerprint, int $expiry): string { $payload = [ 'user_id' => $userId, 'user_type' => $userType, 'fingerprint' => $fingerprint, 'iat' => time(), 'exp' => time() + $expiry, 'jti' => Str::uuid()->toString(), ]; return JWT::encode($payload, config('intaleq.jwt_secret'), 'HS256'); } private function generateApiKeys($user): void { $apiKey = 'intq_' . Str::random(32); $apiSecret = hash('sha256', Str::random(64) . time()); $user->update([ 'api_key' => $apiKey, 'api_secret' => $apiSecret, ]); } private function success(array $data, int $code = 200): JsonResponse { return response()->json(['status' => 'success', 'data' => $data], $code); } private function failure(string $message, int $code = 401): JsonResponse { return response()->json(['status' => 'failure', 'message' => $message], $code); } }