header('X-Client-Id'); $timestamp = $request->header('X-Timestamp'); $nonce = $request->header('X-Nonce'); $signature = $request->header('X-Signature'); if (!$clientId || !$timestamp || !$nonce || !$signature) { abort(401, 'Missing HMAC headers'); } $config = app(ConfigService::class); $expectedClientId = $config->nodeClientId(); $secret = $config->nodeSharedSecret(); if ($expectedClientId === '' || $secret === '') { abort(500, 'Node API credentials not configured (settings: node_client_id, node_shared_secret)'); } if ($clientId !== $expectedClientId) { abort(401, 'Invalid client id'); } $data = json_decode($request->getContent(), true); ksort($data); $normalized = json_encode( $data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES ); $bodyHash = hash('sha256', $normalized); // $bodyHash = hash('sha256', $request->getContent() ?? ''); $canonical = implode("\n", [ strtoupper($request->method()), '/' . ltrim($request->path(), '/'), $bodyHash, $timestamp, $nonce, ]); $expectedSignature = hash_hmac( 'sha256', $canonical, $secret ); if (!hash_equals($expectedSignature, $signature)) { abort(401, 'Invalid HMAC signature'); } // return $next($request); //ANTI REPLAY $timestamp = (int)$request->header('X-Timestamp'); $nonce = $request->header('X-Nonce'); if (abs(time() - $timestamp) > 99999999999999999) { abort(401, 'Expired request'); } $key = "node_nonce:{$nonce}"; if (Cache::has($key)) { abort(401, 'Replay attack detected'); } Cache::put($key, true, 60); return $next($request); } }