rateLimitKey($request); if (RateLimiter::tooManyAttempts($key, self::MAX_ATTEMPTS)) { $retryAfter = RateLimiter::availableIn($key); return response()->json([ 'message' => 'API rate limit exceeded.', ], 429, [ 'Retry-After' => (string) $retryAfter, 'X-RateLimit-Limit' => (string) self::MAX_ATTEMPTS, 'X-RateLimit-Remaining' => '0', ]); } RateLimiter::hit($key, self::DECAY_SECONDS); $response = $next($request); $response->headers->set('X-RateLimit-Limit', (string) self::MAX_ATTEMPTS); $response->headers->set('X-RateLimit-Remaining', (string) RateLimiter::remaining($key, self::MAX_ATTEMPTS)); return $response; } private function rateLimitKey(Request $request): string { $bearerToken = $request->bearerToken(); if ($bearerToken !== null && str_contains($bearerToken, '|')) { [$tokenId] = explode('|', $bearerToken, 2); if (ctype_digit($tokenId)) { return 'api-v1:token:'.$tokenId; } } if ($bearerToken !== null) { return 'api-v1:bearer:'.hash('sha256', $bearerToken); } $token = $request->user()?->currentAccessToken(); if (is_object($token) && method_exists($token, 'getKey') && $token->getKey() !== null) { return 'api-v1:token:'.$token->getKey(); } return 'api-v1:user:'.($request->user()?->getAuthIdentifier() ?? $request->ip()); } }