How to override group middleware? What i want to achieve is to add other throttle limit for register/login routes.
My current throttle is set in kernel.
'api' => [
'throttle:40,1',
'bindings',
],
I want to set new throttle limit for login/register routes.
This is how i did it.
Route::post('login', 'Api\UserController#login')->middleware('throttle:15,3')->name('user.login');
Route::post('register', 'Api\UserController#register')->middleware('throttle:15,3')->name('user.register');
When i run php artisan route:list it says that this middleware api,throttle:15,3 is applied to this route.
The problem is when i run login request, response header says
X-RateLimit-Limit 40
X-RateLimit-Remaining 38
So as far as i see my new middleware is not applied. But my throttle requests are counted twice. How can i apply different middleware for throttle on login/register routes and override the old one ?
Old topic, but its the first i found; time for an updated answer.
I've had this problem in the past as well. My solution back then was to add the middleware in the constructor of the controllers. I dislike it but it works.
I'm currently using Laravel 8 with a new project and found that the following solution works:
Set the default middleware in kernel.php
'api' => [
'throttle:40,1',
'bindings',
],
Remove the middleware throttle:40,1 from the specific route, and add the correct middleware throttle:15,3:
Route::post('login', 'Api\UserController#login')->withoutMiddleware('throttle:40,1')->middleware('throttle:15,3')->name('user.login');
If you do not remove the middleware, it will run the throttle middleware twice per request.
I also played around with $this->middleware( 'throttle:40,1' )->except( ['login'] ) in the constructor of Api\UserController, however that does not give the required result; it will just add the middleware for all but one method, it does not overwrite.
Had this same question and just did some research. It doesn't appear that there is a way to overwrite the middleware configuration.
I, too, see that my middleware has updated in route:list but when resolving the middleware, it always uses a merged set of rules and so that initial api rule will end up overriding anything that defines something else over that.
You have a couple of options:
Remove the throttle rule from the Kernel api middleware definition and then use a Route::group() to re-add that specific rule to the rest of the routes. Then, in the same file, you can create a new Route::group() which defines the custom throttle config.
Route::group(['middleware' => 'throttle:120,1'], function () {
...
});
Route::group(['middleware' => 'throttle:15,3'], function () {
...
});
Create a custom api-auth.php file which is wrapped in a custom middleware group that you define just like the default api middleware. (You'll need to add another call in your RouteServiceProvider to load it like this:
public function map() {
...
$this->mapCustomAuthRoutes();
}
protected function mapCustomAuthRoutes()
{
Route::middleware(['throttle:15,3', 'bindings'])
->namespace($this->namespace)
->as('api.')
->group(base_path('routes/api-auth.php'));
}
Related
I started creating a REST API using the lumen framework and wanted to set up a particular behaviour for my GET /user route. Behaviour is the following:
If the request come from an authenticated user (using auth middleware), the method getAllFields from UserController is called and return all the data from the user
If it's not the case, the method get from UserController is called and return some of the data from the user
It seems logic to me to just write it like that in my web.php using a simple middleware:
<?php
$router->group(['middleware' => 'auth'], function () use ($router) {
$router->get('/user/{id}', [
'uses' => 'UserController#getAllFields'
]);
});
$router->get('/user/{id}', [
'uses' => 'UserController#get'
]);
But for some reason, even if the middleware is correct, I always get the response of the second route declaration (that call get()). I precise that if I remove the second route declaration, the one in the middleware work as expected.
Have someone an idea how I can achieve something similar that work?
Router will check if your request matches to any declared route. Middleware will run AFTER that match, so You cannot just return to router and try to find another match.
To fallow Laravel and Routes pattern - You should have single route that will point to method inside controller. Then inside that You can check if user is logged or not and execute getAllFields() from that controller. It will be not much to rewrite since You are currently using UserController in both routes anyway.
web.php
$router->get('/user/{id}', 'UserController#get');
UserController.php
public function get()
{
return auth()->check() ? YourMethodForLogged() : YourMethodForNotLogged();
}
Or if there is not much logic You can keep this in single method.
Also it is good idea to fallow Laravels REST standards (so use show instead of get, "users" instead of "user" etc - read more https://laravel.com/docs/7.x/controllers)
web.php
$router->get('/users/{user}', 'UserController#show');
UserController.php
public function show(User $user)
{
if (auth()->check()) {
//
} else {
//
}
}
To summary - for your needs use Auth inside controller instead of middleware.
To check if user is logged You can use Facade Auth::check() or helper auth()->check(), or opposite Auth::guest() or auth()->guest().
If you are actually using Lumen instead of full Laravel then there is not auth helper by default (You can make own or use package like lumen-helpers) or just keep it simple and use just Facades instead (if You have then enabled in Lumen).
Read more https://laravel.com/docs/7.x/authentication and https://lumen.laravel.com/docs/7.x/authentication
This pattern is against the idea of Laravel's routing. Each route should be defined once.
You can define your route without auth middleware enabled and then define your logic in the controller.
I am using subdomain routing heavily within my project, as it's a multi-tenant application with each tenant having their own subdomain.
As a result, all of my routes are wrapped in:
Route::domain('{tenant}.'.config('app.base_url'))->group(function () {
// My routes here!
});
To use the route() helper within my code, I need to pass it all of the route parameters associated with it. Every single route has tenant associated with it, so I constantly found myself repeating code and writing route('my-route-name', ['tenant' => $request->route('tenant')]);
I've created a middleware with the following code in it:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use URL;
class SetTenantFromRequest
{
public function handle(Request $request, Closure $next)
{
list($subdomain) = explode('.', $request->getHost(), 2);
URL::defaults(['tenant' => $subdomain]);
return $next($request);
}
}
And placed it in the HTTP Kernel.php file like so:
protected $middleware = [
TrustProxies::class,
CheckForMaintenanceMode::class,
ValidatePostSize::class,
TrimStrings::class,
ConvertEmptyStringsToNull::class,
WebHeaders::class,
SetLanguage::class,
SetTenantFromRequest::class,
];
// Removed for brevity...
protected $middlewarePriority = [
StartSession::class,
ShareErrorsFromSession::class,
SetTenantFromRequest::class,
Authenticate::class,
ThrottleRequests::class,
AuthenticateSession::class,
SubstituteBindings::class,
Authorize::class,
];
I had to add it to the $middlewarePriority array as it needs to run before the Authenticate middleware. Since Authenticate calls return route('login');, I need that URL parameter available beforehand.
Now with doing all of this, I still get the following error thrown from the Authenticate middleware: Missing required parameters for [Route: login] [URI: login].
If I run ddd(URL::getDefaultParameters()); within the Authenticate middleware, it prints an empty array. However if I run the same ddd(...) within the SetTenantFromRequest middleware, it shows the tenant in there as I expect it to be.
Does anyone have an idea on how I can solve this problem?
Moving this from the global middleware stack to the web middleware stack alleviates the issue.
This forces me to add the tenant parameter to the route(...) call within my Authenticate middleware, but it does allow me to forgo it everywhere else.
I would like to have a Slim middleware to check authentication on all requests but some specific ones (for example login page).
I prepared the first AuthenticationMiddleware middleware to check all pages:
$app->add(new \App\Middleware\AuthenticationMiddleware($container));
Then I create another one AnonymousMiddleware that it is supposed to add a variable to set the exception to authentication checks:
$app->group('',function() use ($app){
$app->get('/','LogicController:index');
})->add(new AnonymousMiddleware($container));
The problem is that routes middleware (AnonymousMiddleware) is applied AFTER the general middleware (AuthenticationMiddleware);
I tried to use determineRouteBeforeAppMiddleware but it doesn't change the result.
I know I would set a route middleware for all authenticated routes but sounds a bit dangerous if I forget it, so, I would prefer to set which router are not under authentication then keep the check all other routes.
you need to chose different approach
you need to wrap all routes which should be "protected via Auth middleware" and exclude = not wrap routes which should not be handled by this middleware
you can add a group where all your routes will live and only login (and logout) route will be outside of this group ;)
something like
$app->group("/api/v1", function() {
// all your protected routes definitions here
})
->add(AuthenticationMiddleware::class)); // wrap by middleware
$app->post('/login', function(){});
$app->add(new MiddlewareForAllRoutes()); // middlewares for all routes
I am running Laravel 5.4 and have my API routes setup with an API middleware that verifies an authentication token sent in the headers.
However, I want to avoid, or prevent the api/Login (route that generates the auth token) from being subject to the middleware.
Currently, in my API middleware, before any logic happens I have:
if(strpos($request->getUri(), 'Login')):
return $next($request);
endif;
I would like to remove checking if the route is the Login route before proceeding with the middleware logic. Is there a native way in Laravel to accomplish the above?
Note: all API routes are protected via an API middleware group which I have created in the Http/Kernel, then added the in the RouteServiceProvider.
You could add an except property in your middleware
Route::group(['middleware' => ['api'], 'except' => 'Login'], function () {
// Your Routes
});
Hi have a middleware that i must ensure is run after the auth middleware. How can i guarantee that the auth middleware is processed before mine? Is this even possible?
Thanks in advance.
from the laravel 5.4 documentation:
Route::get('/', function () {
//
})->middleware('first', 'second');
First and second must be route middleware, if you look in app/Http/Kernel.php you can find them.
Your middlware will be run after, Auth middleware have a higher priority. See variable $middlewarePriority of your Kernel base class (Illuminate\Foundation\Http\Kernel).
First middleware is StartSession and this is what we want.
If you want, you can modify this variable to run one of your middleware before Auth.