Reputation: 7447
I have an API that is using Laravel that is being called from another instance of Laravel with Guzzle.
The second server's IP address is triggering the throttle on the API.
I would like to pass through the user's domain and IP address from the second server to the API. I am hoping not to recode the Throttle middleware.
I am wondering if anyone has faced this before and if so how they solved it.
The middleware group on the API is set up like this
/**
* The application's route middleware groups.
*
* @var array
*/
protected $middlewareGroups = [
'api' => [
'throttle:60,1',
\Barryvdh\Cors\HandleCors::class,
'bindings',
],
];
/**
* Resolve request signature.
*
* @param \Illuminate\Http\Request $request
* @return string
*
* @throws \RuntimeException
*/
protected function resolveRequestSignature($request)
{
if ($user = $request->user()) {
return sha1($user->getAuthIdentifier());
}
if ($route = $request->route()) {
return sha1($route->getDomain().'|'.$request->ip());
}
throw new RuntimeException('Unable to generate the request signature. Route unavailable.');
}
Upvotes: 4
Views: 3209
Reputation: 2701
The out of the box solution, if you are using a version >= 5.6, is to use the dynamic rate limit.
Dynamic Rate Limiting
You may specify a dynamic request maximum based on an attribute of the authenticated User model. For example, if your User model contains a rate_limit attribute, you may pass the name of the attribute to the throttle middleware so that it is used to calculate the maximum request count:
Route::middleware('auth:api', 'throttle:rate_limit,1')->group(function () { Route::get('/user', function () { // }); });
/**
* Resolve the number of attempts if the user is authenticated or not.
*
* @param \Illuminate\Http\Request $request
* @param int|string $maxAttempts
* @return int
*/
protected function resolveMaxAttempts($request, $maxAttempts)
{
if (Str::contains($maxAttempts, '|')) {
$maxAttempts = explode('|', $maxAttempts, 2)[$request->user() ? 1 : 0];
}
if (! is_numeric($maxAttempts) && $request->user()) {
$maxAttempts = $request->user()->{$maxAttempts};
}
return (int) $maxAttempts;
}
Thus, you could add a rate_limit
property in the user (representing the second server) and pass a bigger number
If you don't want to have the caller authenticated, you can easily overwrite the resolveMaxAttempts
method to calculate the limit dynamically based on the request data (you could use any parameter, the host, the ip, etc):
protected function resolveMaxAttempts($request, $maxAttempts)
{
if (in_array(request->ip(), config('app.bypassThrottleMiddleware')) {
return PHP_INT_MAX;
}
return parent::resolveMaxAttempts($request, $maxAttempts);
}
and in your config/app.php
add:
'bypassThrottleMiddleware' => ['0.0.0.0'],
Upvotes: 0
Reputation: 1718
You can pass the client's IP address with the X_FORWARDED_FOR
header, that way the IP address of the second server is not blocked.
Route::get('/', function (Request $request) {
$client = new \GuzzleHttp\Client();
$request = $client->request('GET', '/api/example', [
'headers' => ['X_FORWARDED_FOR' => $request->ip()]
]);
$response = $request->getBody();
});
On your main server you need to add your second server as a trusted proxy (docs) to App\Http\Middleware\TrustProxies
in order to take the IP from this header.
class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*
* @var array
*/
protected $proxies = [
'192.168.1.1', // <-- set the ip of the second server here
];
//...
}
Now every call to $request->ip()
on the main server will have the original client IP instead of the second server's IP. That will also affect the throttling.
Upvotes: 6
Reputation: 1
if ($route = $request->route()) {
return sha1($route->getDomain().'|'.$request->ip());
Upvotes: -3