The delivered auth middleware that comes with Laravel 5 is great for user-only routes and controllers, however I want to add the ability to also check if the user is an administrator.
Currently, in my controllers, I have this for every class:
if (Auth::user()->level <= 1) {
// admin can do these actions
} else {
// redirect
}
It's very redundant and I'd like to look at what my options are. Since I want to retain the original auth middleware for user authentication, should I be building a new one for administrator authentication, or can I make some simple change in the original auth middleware that can account for my code above?
Middleware in Laravel 5.0 do not support arguments (this will be added in the upcoming 5.1 release).
Your options are to either create a separate middleware for this, or use route filters.
You can create a route filter by putting this in your RouteServiceProvider's boot method:
$router->filter('userLevel', function($route, $request, $level)
{
$user = auth()->user();
if ( ! $user || $user->level > $level)
{
return response('Unauthorized', 401);
}
});
Then use that filter in your routes:
Route::group(['before' => 'userLevel:1'], function($router)
{
// define routes...
});
Related
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');
}
I have a requirement where there is an API method (guarded by the well-known package tymon/jwt-auth) and I need to also be able to access it using the basic session based web middleware.
I don't want to repeat the route in both api.php and web.php even though that would totally work.
I tried adding both to the route but they simply don't work, like: ['auth:api', 'web']
I also tried creating a new middleware with the intention of checking both api and web like so:
class CombinedAuth
{
public function handle($request, Closure $next)
{
$web = Auth::guard('web')->user();
if ($web) {
return $next($request);
}
$api = Auth::guard('api')->user();
if ($api) {
return $next($request);
}
if(!$web && !$api) {
throw new AuthorizationException;
}
}
}
and that also doesn't work. The api middleware works fine but the web middleware doesn't and it always signs me out and redirects to the login page.
So Is there a neat way of protecting a route with api and web middlewares at the same time in Laravel 5.8?
You can use 'auth:api,web' to check for multiple guards.
Using multiple can middleware calls in Laravel 9 route;
<?php
Route::get('/', function () {
return view('money');
})
->middleware([
'can:manage,App\Models\Payment',
'can:manage,App\Models\Withdraw',
]);
?>
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.
Currently to add authentication I use this in my routes file:
Route::middleware(['auth'])->group(function () {
});
But I want to also check for different routes if the user is an admin, so currently I do this and have a custom middleware file:
Route::middleware(['auth', 'admin'])->group(function () {
});
//
<?php
namespace App\Http\Middleware;
use Closure;
class Admin {
public function handle($request, Closure $next)
{
if ( Auth::check() && Auth::user()->isAdmin() )
{
return $next($request);
}
return redirect('dashboard');
}
}
This all works fine, but I noticed with the api middleware it uses this:
Route::middleware('auth:api')->get('/user', function (Request $request) {
return $request->user();
});
Questions
How can I make a namespaced auth middleware like the api one 'auth:admin'
Where is the file located that sets the 'auth:api' middleware, I could not find it anywhere in the app folder
Is there any other ways to do multiple auth like editing the file config/auth.php and then separating the users between two tables, one for admins and one for other users.
auth:api is actually the basic auth middleware with the string api as a parameter. This means the user is authenticated using the api authentication guard
You could use auth:admin out of the box if you added a custom authentication guard for the admins.
The auth middleware accepts one parameters which is the guard(s) to use. As you can see in Illuminate\Auth\Middleware\Authenticate middleware.
You can add custom auth guards. So you can create auth:admin if you'd like. But I do think it's perfectly fine to use one middleware to verify that the user is who he is (authentication) and a second one to verify that the user is allowed to visit the page he/she wants to visit (authorization).
I'm using Laravel 5 on a Windows dev machine. I want to customize and use the Auth middleware throughout my application, to maintain authentication.
My use case is a standard one. There are two (or three) classes of users - Admin and Regular (regular would be all users that are not admin).
The Admin has the obvious role of backend management, and hence has a separate routing group /admin/, which should redirect an unlogged user to /admin/login. I have set it up like so..
Route::group(['middleware'=>'auth', 'prefix' => 'admin'], function() {
Route::get('login','App\AuthController#getLogin');
Route::post('login','App\AuthController#postLogin');
});
When the login form is posted, how do I ask Auth to add a filter
either that only validate from among those users where 'is_admin' is true?
or ask it to first join a User and a UserRoles table to identify only users with an Admin role?
I recommend you to define another middleware that detects if user is admin instead of modifying the auth. Now add this another middleware to your routes that only admins can access.
Add several middleware to route like this
Route::group(['middleware' => ['auth','admin']], function() {
Middleware will look something like
public function handle($request, Closure $next) {
if (Auth::user()->role == "admin") {
return $next($request);
} else {
return redirect("/")->withMyerror("You are not authorized for this action");
}
}
Why not instead of dealing with the Auth filter and trying to "validate" only on a certain condition, in your login code, just check what's the type of the user?
This is my high level code of doing it:
// get roles which are allowed to login to the admin panel
$roles = $this->userService->adminRoles();
$user = User::whereUsername(Input::get('username'))->whereIn('role_id', $roles)->first();
if (is_null($user)) {
// ...
}
// create our user data for the authentication
$userdata = array(
'username' => Input::get('username'),
'password' => Input::get('password'),
);
// attempt to do the login
// Auth::attempt($userdata) ....
This way you only do it once when you attempt the login and that's it?