How can i make sure middleware is run before another - php

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.

Related

Declare same route twice but expect different behaviour according to a middleware

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.

Slim Authentication middleware for all routes except someone

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

Laravel override group middleware

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'));
}

Multiple middleware not working on laravel

I have a route resource group that can only be accessible by one of 2 middleware rules. I have registered them both and they both work independently if I test them both out alone, but when I have them together they don't work
I have tried running them both as either an "or" statement (which means the middleware works as intended) but this means that anyone not logged in can also access the routes for some reason. If I use a comma to separate the middleware, it's blocked for everyone. I know both middleware works ok as they do work if I try them independently. I am using the below code
Route::group(['middleware' => ['IsAdmin' or 'IsPatreon']], function(){
Route::resource('patreon', 'patreonGalleryController', ['names'=>[
'index'=>'patreonGallery.index',
'create'=>'patreonGallery.create',
'store'=>'patreonGallery.store',
'edit'=>'patreonGallery.edit',
'show'=>'patreonGallery.show',
'destroy'=>'patreonGallery.destroy',
]]);
});
How can I set it so that only either admin or patreon uses can see the paths?
Two middlewares are working separately.
IsAdmin is checking that user is admin
IsPatreon is checking that user is patreon...
You cannot merge these 2 middlewares by OR Operator
Probably you need to create new middelware, something like
IsAdminOrPatreon and do you checks inside of that middleware and assing that middleware to your Group..
Or you can try with middleware parameters, for example
Route::group(['middleware' => ['checkRoles:admin,patreon']], function(){
Route::resource('patreon', 'patreonGalleryController', ['names'=>[
'index'=>'patreonGallery.index',
'create'=>'patreonGallery.create',
'store'=>'patreonGallery.store',
'edit'=>'patreonGallery.edit',
'show'=>'patreonGallery.show',
'destroy'=>'patreonGallery.destroy',
]]);
});
And in you checkRoles middleware get the admin and patreaon roles like this:
public function handle($request, Closure $next) {
// will contain ['role1', 'role2']
$allowedRoles = array_slice(func_get_args(), 2);
// here you can loop and check your roles
}
Note! If you pass 'checkRoles:admin,patreon' you will get
array(admin,patreon)
If you pass 'checkRoles:admin' you will get
array(admin)
you can't use or condition inside middleware array. middleware array always return and condition. you can specify the user role inside your middleware.
gist sample role middleware
https://gist.github.com/ivanhoe011/931417be3e36b3f06e994bfe5cd004f9
You do something like this in your controller.
public function __construct()
{
return ($this->middleware('IsAdmin')) || $this->middleware('IsPatreon');
}
Each route on this controller will be authenticated by any one of middleware.

Laravel Route::current() returns null

I am trying to use the current route in middleware in Laravel 5.7 using the following line of code:
$route = Route::current();
But I get a null value for $route. Any ideas?
The route couldn't be available yet because the router hasn't been yet called. That's depends on what middlewares are called before your middleware.
I think that, in a before middleware, you can try with: $route = $request->path(); just to be sure and not depending on the Router being booted or not.
You just want to change where your middleware is registered in app/Http/Kernel.php if you need access to the Route facade.
I'm betting that your middleware is in the protected $middleware array. It should be in one of your $middlewareGroups usually web or api. Or if you need it for a specific route you can add it to the $routeMiddleware array.
For example, I wanted to access Route from within my HandleInertiaRequests middleware so I had to move to $middlewareGroups like shown in this screenshot:

Categories