Apply middleware conditionally based on auth in Laravel - php

I am trying to achieve the following:
Create a route with 2 groups of middleware. 1 applies when not logged in and the other applies when you are.
The auth middleware obviously catches this but I want to be able to reach the route without auth.
Route::get('my-route', ...)
->middleware('not-auth')
->middleware('when-authed')
->name('my.route');
The route should be accessible when not logged in with NO other middleware applied
The route should have other middleware applied when logged in which will include auth:sanctum checks
I am happy to apply custom logic in the controller or the routes file
I have tried using the constructor in the controller but auth is not available to check if the user is logged in at time of instantiation.
As an example of something i have tried to demonstrate what I am trying to achieve, see below:
$authMiddleware = [
'auth:sanctum',
config('jetstream.auth_session'),
'verified',
'shared.ui'
];
Route::get('/route', [class::class, 'index'])
->middleware((auth()->check()) ? $authMiddleware : [])
->name('route');

Right, I have rewritten the verified middleware to resolve my issue and applied it as a different middleware on the route. This then resolves the issue I had where you had to be logged in with the middleware.
See below:
public function handle($request, Closure $next, $redirectToRoute = null)
{
//removed from start of if statement on original verify middlware !$request->user() ||
if (($request->user() instanceof MustVerifyEmail &&
! $request->user()->hasVerifiedEmail())) {
return $request->expectsJson()
? abort(403, 'Your email address is not verified.')
: Redirect::guest(URL::route($redirectToRoute ?: 'verification.notice'));
}
return $next($request);
}
Added alias to route middleware
'auth.verified' => \App\Http\Middleware\EnsureEmailIsVerifiedWhenLoggedIn::class,
Applied auth.verified on the route in question
Thank you to ericmp for suggesting I should be looking at doing this a different way and thanks to the rest of you for your comments.

Related

Laravel: Check if User is an admin using isAdmin column?

I'm really new to laravel and have been reading the documentations and tutorials. I'm planning on making an app that has 2 roles, admin and user. I modified my User model to have the column 'isAdmin' with boolean value since I only need 2 roles. How do I perform a check on this attribute during auth? Thank you.
TO answer your question, first of all to make protect any route using the auth middleware which ensures a user is authenticated (logged in) before they can access the route, you simply need to add the auth middleware.
e.g
web.php
<?php
Route::middleware('auth')->group(function(){
//All Routes which needs user to be logged in
});
or
//Individiual Route Middleware
Route::get('/path/to', 'controller#instance')->middleware('auth');
As for checking user role, you can basically create a middleware for this using the following steps:
run your php artisan make:middleware IsAdminMiddleware
open your IsAdminMiddleware and add this code inside the handle function:
public function handle($request, Closure $next)
{
if(!Auth::check()){
return redirect()->route('login');
}
if(Auth::user()->isAdmin == true){
return $next($request);
}
return redirect()->back()->with('unauthorised', 'You are
unauthorised to access this page');
}

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 Multiple Middleware in Route with OR Condition

I wonder if I can do this in Laravel Route. Let's say I have Admin, Premium and User (which can be login too by using Auth) Middleware. Also, I have controller with methods like this: index, create, edit, delete and I want Admin to be able do all those things, but Premium can only be able to access index method, and User can't access anything in this controller (he can access another controller). I know I can use except or only middleware method like this:
public function __construct()
{
$this->middleware('premium')->only('index');
$this->middleware('admin');
// or maybe $this->middleware('admin')->except('index');
}
but when I try to put these two middlewares in __construct method they will start to conflict each other, it makes sense because index method can be access by Premium but then can't be access by the Admin itself. By the way, my middleware is simply checking:
if (Auth::check()) {
if (Auth::user()->role == 'Admin') {
return $next($request);
}
}
return redirect('/home');
So, back to my question, can I have OR Middleware so I can avoid conflict from multiple middleware (which is must be AND condition when they written at the same controller constructor)?
If you change up the way your logic is thinking a little bit, the answer becomes pretty easy. You can create new middleware that checks if it can access the specific method.
So create the following middleware 'CanAccessIndex':
if (Auth::check()) {
if (Auth::user()->role == 'Admin' || Auth::user()->role == 'Premium') {
return $next($request);
}
}
return redirect('/home');
Then, you can put that middleware on the index function (instead of the premium middleware) and put your admin middleware on everything EXCEPT index. Like so:
public function __construct()
{
$this->middleware('canAccessIndex')->only('index');
$this->middleware('admin')->except('index');
}
That's one way to do it.
You need middleware group for this, and to manage these hierarchy of access layer, you can simply use Route grouping.
I will demo an example of what I mean:
Say you have auth middleware general for authenticated users (i.e everybody), then another called premium for premium member, and admin for the Admin.
Then you'll group based on the access level:
Route::middleware('auth')->group(function(){
Route::middleware('premium')->group(function(){
Route::post('/create', 'HomeController#create')->middleware('admin');
Route::put('/update/{id}', 'HomeController#update')->middleware('admin');
Route::get('/index', 'HomeController#index');
Route::put('/delete/{id}', 'HomeController#delete')->middleware('admin');
});
});
So you can have general check in your middleware with a check. It would have been much easier if you have role level say 3 for admin and 2 for premium member. So we can have for the premium middleware:
public function handle($request, Closure $next)
{
return auth()->user->role >= 2
? $next($request)
: redirect('/home');
}
This is just an example. You can do further check based on your need but more importantly, ensure your admin middleware to checks the exact role level that is allowed.

laravel routes , how to redirect if statement , routes.php

i have in routes.php user and admin routes , how to can access admin routes just if Session::get('admin')==1 ?
enter image description here
You need to define a middleware, for example:
namespace App\Http\Middleware;
use Closure;
class CheckAge
{
public function handle($request, Closure $next)
{
if (!session()->has('admin') || session('admin') != 1) {
return redirect('/');
}
return $next($request);
}
}
Register it and apply it to routes you want to protect.
Doesn't look like you have a specific problems in terms of code, so I'm just going to point you to the documentation on Middleware.
Middleware get's executed before it reaches the controller, therefore you can apply your logic there, simply create a new middleware, add an if statement but instead of returning $next return response() with your appropriate response should the middleware fail.
Laravel Docs

Multiple middleware has permittion on same routes

I have multi middleware (studen, parent, admin) and create some route group with that middleware. But some route can access if user is in any of that groups and belong in any of that middleware but not if is in other middleware fot example teacher. I use something like that in docs: http://laravel.com/docs/5.1/routing#route-groups But it's work when I put one route, when add another route group with another middleware it doesn't work. Is that possible and how to make it?
When I execute php artisan route it gives me an error
[Symfony\Component\Debug\Exception\FatalErrorException]
Call to a member function inRole() on null
Laravel's route middleware is executed one by one as declared in routes.php file. Therefore, if one of them denies access by throwing an exception or returning some respoise, the next middlewares won't be executed.
In order to make that work, you'll need a single middleware that would check if current user has any of required roles. Luckily, as of Laravel 5.1 you are able to pass parameters to middleware from your routes.php file (see http://laravel.com/docs/5.1/middleware#middleware-parameters), so you'll only need one middleware class to handle all cases.
Example middleware class could look like that:
class HasAnyRole
{
public function handle($request, Closure $next, $roles)
{
// Return Not Authorized error, if user has not logged in
if (!$request->user) {
App::abort(401);
}
$roles = explode(',', $roles);
foreach ($roles as $role) {
// if user has given role, continue processing the request
if ($request->user->hasRole($role)) {
return $next($request);
}
}
// user didn't have any of required roles, return Forbidden error
App::abort(403);
}
}
Register the middleware in your Kernel.php:
protected $routeMiddleware = [
'has_any_role' => 'App\Http\Middleware\HasAnyRole',
];
Now, in your routes.php you can apply the middleware to a group like that:
//this route is available only to users with role admin or author
Route::put('post/{id}', ['middleware' => 'has_any_role:admin,author', function ($id) {
//
}]);
This should do the trick, just make sure that your User class has a hasRole method.

Categories