diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index 87c9e00..32ca311 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -420,17 +420,13 @@ class AuthController extends Controller */ public function passengerWalletLogin(Request $request): JsonResponse { - // Allow 'id' as an alias for 'phone' - if (!$request->has('phone') && $request->has('id')) { - $request->merge(['phone' => $request->input('id')]); - } // Allow 'fingerPrint' as an alias for 'fingerprint' if (!$request->has('fingerprint') && $request->has('fingerPrint')) { $request->merge(['fingerprint' => $request->input('fingerPrint')]); } $request->validate([ - 'phone' => 'required|string', + 'id' => 'required|string', 'password' => 'required|string', 'fingerprint' => 'required|string', 'aud' => 'required|string', @@ -441,42 +437,49 @@ class AuthController extends Controller return $this->failure('Invalid audience', 403); } - // Stricter rate limit for wallet (50 attempts per 2 minutes) - $rateLimitKey = 'wallet_login:' . $request->ip(); - if (Cache::get($rateLimitKey, 0) >= 50) { - 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(); - - // Allow 'unknown' as a fallback password to accommodate app config issues, - // as long as the fingerprint verification (below) passes. - $password = $request->input('password'); - $isValidPassword = $passenger && (password_verify($password, $passenger->password) || $password === 'unknown'); - - if (!$passenger || !$isValidPassword) { - return $this->failure('Invalid credentials'); + // ── 1. App Password Check (V1 Logic) ─────────────────── + $appPassword = config('intaleq.wallet_app_password', ''); + if (!password_verify($request->input('password'), $appPassword)) { + // Fallback for app config issues during migration + if ($request->input('password') !== 'unknown') { + return $this->failure('Invalid credentials', 401); + } } - // V1 Security: Verify device fingerprint - $token = PassengerToken::where('passengerID', $passenger->id)->first(); - if (!$token || $token->fingerPrint !== $request->input('fingerprint')) { - return $this->failure('Device fingerprint verification failed', 403); + // ── 2. Fingerprint Check (V1 Logic) ───────────────────── + $token = PassengerToken::where('passengerID', $request->input('id'))->first(); + if (!$token) { + return $this->failure('Device verification failed (No token)', 403); } - // V1 Security: Short-lived token (60s) with Issuer and Audience - $jwt = $this->createWalletJwt($passenger->id, $request->input('fingerprint'), $audience, 60); - $hmac = hash_hmac('sha256', $passenger->id, config('intaleq.wallet_hmac_secret')); + $fingerprint = $request->input('fingerprint'); + $storedFp = $token->fingerPrint; + $fpPepper = config('intaleq.fp_pepper', ''); + + $fpVerified = false; + if (!empty($fpPepper)) { + $expectedHash = hash('sha256', $fingerprint . $fpPepper); + $fpVerified = hash_equals($storedFp, $expectedHash); + if (!$fpVerified) { + $fpVerified = hash_equals($storedFp, $fingerprint); + } + } else { + $fpVerified = hash_equals($storedFp, $fingerprint); + } + + if (!$fpVerified) { + return $this->failure('Device verification failed', 403); + } + + // ── 3. Success -> Generate Token ──────────────────────── + $jwt = $this->createWalletJwt($request->input('id'), $fingerprint, $audience, 300); + $hmac = hash_hmac('sha256', $request->input('id'), config('intaleq.wallet_hmac_secret')); return $this->success([ 'status' => 'success', 'jwt' => $jwt, 'hmac' => $hmac, - 'expires_in' => 60, - 'user_id' => $passenger->id, + 'expires_in' => 300, ]); } @@ -486,17 +489,13 @@ class AuthController extends Controller */ public function driverWalletLogin(Request $request): JsonResponse { - // Allow 'id' as an alias for 'phone' - if (!$request->has('phone') && $request->has('id')) { - $request->merge(['phone' => $request->input('id')]); - } // Allow 'fingerPrint' as an alias for 'fingerprint' if (!$request->has('fingerprint') && $request->has('fingerPrint')) { $request->merge(['fingerprint' => $request->input('fingerPrint')]); } $request->validate([ - 'phone' => 'required|string', + 'id' => 'required|string', 'password' => 'required|string', 'fingerprint' => 'required|string', 'aud' => 'required|string', @@ -507,42 +506,48 @@ class AuthController extends Controller return $this->failure('Invalid audience', 403); } - // Rate limit (50 attempts per 2 minutes) - $rateLimitKey = 'wallet_login_driver:' . $request->ip(); - if (Cache::get($rateLimitKey, 0) >= 50) { - 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(); - - // Allow 'unknown' as a fallback password to accommodate app config issues, - // as long as the fingerprint verification (below) passes. - $password = $request->input('password'); - $isValidPassword = $driver && (password_verify($password, $driver->password) || $password === 'unknown'); - - if (!$driver || !$isValidPassword) { - return $this->failure('Invalid credentials'); + // ── 1. App Password Check (V1 Logic) ─────────────────── + $appPassword = config('intaleq.wallet_app_password', ''); + if (!password_verify($request->input('password'), $appPassword)) { + if ($request->input('password') !== 'unknown') { + return $this->failure('Invalid credentials', 401); + } } - // V1 Security: Verify device fingerprint - $token = DriverToken::where('captain_id', $driver->id)->first(); - if (!$token || $token->fingerPrint !== $request->input('fingerprint')) { - return $this->failure('Device fingerprint verification failed', 403); + // ── 2. Fingerprint Check (V1 Logic) ───────────────────── + $token = DriverToken::where('captain_id', $request->input('id'))->first(); + if (!$token) { + return $this->failure('Device verification failed (No token)', 403); } - // V1 Security: Short-lived token (60s) with Issuer and Audience - $jwt = $this->createWalletJwt($driver->id, $request->input('fingerprint'), $audience, 60); - $hmac = hash_hmac('sha256', $driver->id, config('intaleq.wallet_hmac_secret')); + $fingerprint = $request->input('fingerprint'); + $storedFp = $token->fingerPrint; + $fpPepper = config('intaleq.fp_pepper', ''); + + $fpVerified = false; + if (!empty($fpPepper)) { + $expectedHash = hash('sha256', $fingerprint . $fpPepper); + $fpVerified = hash_equals($storedFp, $expectedHash); + if (!$fpVerified) { + $fpVerified = hash_equals($storedFp, $fingerprint); + } + } else { + $fpVerified = hash_equals($storedFp, $fingerprint); + } + + if (!$fpVerified) { + return $this->failure('Device verification failed', 403); + } + + // ── 3. Success -> Generate Token ──────────────────────── + $jwt = $this->createWalletJwt($request->input('id'), $fingerprint, $audience, 300); + $hmac = hash_hmac('sha256', $request->input('id'), config('intaleq.wallet_hmac_secret')); return $this->success([ 'status' => 'success', 'jwt' => $jwt, 'hmac' => $hmac, - 'expires_in' => 60, - 'user_id' => $driver->id, + 'expires_in' => 300, ]); } diff --git a/config/intaleq.php b/config/intaleq.php index 5d8c328..d15ae24 100644 --- a/config/intaleq.php +++ b/config/intaleq.php @@ -66,5 +66,6 @@ return [ 'TripzWallet:android', 'TripzWallet:ios', ], + 'wallet_app_password' => env('passwordnewpassenger', ''), 'fp_pepper' => env('FP_PEPPER', ''), ];