header('X-API-Key'); $timestamp = $request->header('X-Timestamp'); $signature = $request->header('X-Signature'); $nonce = $request->header('X-Nonce'); // 1. Check required headers if (!$apiKey || !$timestamp || !$signature) { return response()->json([ 'status' => 'failure', 'message' => 'Missing authentication headers' ], 401); } // 2. Validate timestamp (prevent replay attacks) $tolerance = (int) config('intaleq.hmac_tolerance', 300); $timeDiff = abs(time() - (int) $timestamp); if ($timeDiff > $tolerance) { return response()->json([ 'status' => 'failure', 'message' => 'Request expired' ], 401); } // 3. Check nonce uniqueness (if provided) if ($nonce) { $nonceKey = "nonce:{$nonce}"; if (Cache::has($nonceKey)) { return response()->json([ 'status' => 'failure', 'message' => 'Duplicate request' ], 401); } // Store nonce for double the tolerance window Cache::put($nonceKey, true, $tolerance * 2); } // 4. Lookup API secret from database $credentials = $this->getApiCredentials($apiKey); if (!$credentials) { return response()->json([ 'status' => 'failure', 'message' => 'Invalid API key' ], 401); } // 5. Reconstruct and verify HMAC signature $payload = $request->getContent(); $message = "{$timestamp}|{$apiKey}|{$payload}"; $expectedSignature = hash_hmac(self::ALGORITHM, $message, $credentials->api_secret); if (!hash_equals($expectedSignature, $signature)) { return response()->json([ 'status' => 'failure', 'message' => 'Invalid signature' ], 401); } // 6. Attach user info to request for controllers $request->merge([ '_auth_user_id' => $credentials->user_id, '_auth_user_type' => $credentials->user_type, ]); return $next($request); } /** * Get API credentials with Redis caching (5 min) */ private function getApiCredentials(string $apiKey): ?object { $cacheKey = "api_cred:{$apiKey}"; return Cache::remember($cacheKey, 300, function () use ($apiKey) { // Check both driver and passenger tables for API keys $driver = DB::connection('primary') ->table('driver') ->select('id as user_id', 'api_secret') ->selectRaw("'driver' as user_type") ->where('api_key', $apiKey) ->where('status', 'notDeleted') ->first(); if ($driver) return $driver; $passenger = DB::connection('primary') ->table('passengers') ->select('id as user_id', 'api_secret') ->selectRaw("'passenger' as user_type") ->where('api_key', $apiKey) ->where('status', 'notDeleted') ->first(); if ($passenger) return $passenger; // Check admin users $admin = DB::connection('primary') ->table('adminUser') ->select('id as user_id', 'api_secret') ->selectRaw("'admin' as user_type") ->where('api_key', $apiKey) ->first(); return $admin; }); } }