Prevent users to access some routes in Laravel and Vue - laravel

I'm using building an SPA using Laravel and Vue and I don't want users to access the /products/create route I've tried using Laravel middlewares but it didn't help
Here's my App.vue component
<template>
<div>
<Navbar :name="user.name"/>
<router-view></router-view>
</div>
</template>
<script>
import Navbar from "./Navbar";
export default {
name: "App",
props: [
'user'
],
components: {
Navbar,
},
created() {
window.axios.interceptors.request.use(config => {
config.headers.common['Authorization'] = 'Bearer ' + this.user.api_token;
return config;
});
},
}
</script>
IsAdmin.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class IsAdmin
{
public function handle($request, Closure $next)
{
if (! Auth::user()->isAdmin) {
return response()->json(['error' => 'Unauthorized'], 403);
}
return $next($request);
}
}
How do I redirect non-authorized users to a 404 page?

For vue routes protection
To protect vue routes you can use navigation guards which are a specific feature within Vue Router that provide additional functionality pertaining to how routes get resolved.
You must be using vue-router package to use routes in vuejs app
In src/router/index.js , you can add route guard as following
import Vue from "vue";
import Router from "vue-router";
import Main from "#/components/Main";
import Products from "#/components/Products";
import Create from "#/components/Create";
import Show from "#/components/Show";
import Unauthorised from "#/components/Unauthorised";
//use vue router
Vue.use(Router);
//init Router and define some routes
export default new Router({
routes: [
{
path: '/',
name: 'Main',
component: Main
},
{
path: '/products',
name: 'Products',
component: Products
},
{
path: '/create',
name: 'Create',
component: Create
},
{
path: '/show',
name: 'Show',
component: Show
},
{
path: '/unauthorised',
name: 'Unauthorised',
component: Unauthorised
}
]
})
//apply route guard
router.beforeEach((to, from, next) => {
//list of blocked routes
const protectedRoutes = ['/products', '/create'];
//the route user is trying to access is in blocked routes list
if (protectedRoutes.includes(to.path)) {
//redirect to route having unauhorised message page
return next('/unauthorised');
}
)
else
{
// otherwise allow user to access route
return next();
}
In this example there are five routes i.e / , /products , /create , /show and last one /unauthorised to show error. Here, if any user tries to access routes listed in $protectedRoutes then they will be redirected to /unauthorised route otherwise allowed to access other routes
you can learn more about vue router guard here and about vue-router here.Additionally, you can guard your routes based on user roles or any other conditions.I recommend you to use vuex to manage user state and manage routes access based on role stored on user state

You're not providing enough information, but the way I do it is using Laravel policies.
I would setup a policy for Products like this:
namespace App\Policies;
use App\Product;
use App\User;
use Illuminate\Auth\Access\HandlesAuthorization;
class ProductPolicy
{
use HandlesAuthorization;
/**
* Determine whether the user can create products.
*
* #param \App\User $user
* #return mixed
*/
public function create(User $user)
{
return $user->hasPermissionTo('create products');
}
}
Register your policies in the App\Providers\AuthServiceProvider.php
protected $policies = [
'App\Product' => 'App\Policies\ProductPolicy',
];
Then in your Product controller you will need to add this in order to got through the authorization process:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Product;
class ProductController extends Controller
{
public function store(Request $request)
{
$this->authorize('create', Product::class)
// The current user is authorized to make this request.
}
}
You will probably like to prevent unauthorized users to even have access your vue route where you create products. In order to do so you would have to pass the users permissions into your vue app.
return [
'name' => $user->name,
'permissions' => [
'createProducts' => $user->can('create', \App\Product::class)
]
}
Then in your vue app:
<router-link v-if="user.permissions.createProducts" to="/products/create">
New Product
</router-link>

Related

How can I add OR relationship in laravel middleware for a Route

I'm using spatie/laravel-permission to add permissions for my routes.
One of the routes I have can be accessed by two permissions (earnings, financial_fund). The user doesn't need to have both permissions to access the controller. He can access the controller by having one of the permissions or both of them.
I've tried to write something like this.
Route::group(['middleware' => ['can:earnings']], function () {
Route::get('/payment', [PaymentsController::class, 'getAll']);
Route::post('/payment/cash', [PaymentsController::class, 'addCashPayment']);
});
Route::group(['middleware' => ['can:financial_fund']], function () {
Route::get('/payment', [PaymentsController::class, 'getAll']);
Route::post('/payment/cash', [PaymentsController::class, 'addCashPayment']);
});
But the above code allows only users with can:earnings permission to access the routes, it doesn't allow users with can:financial_fund permission.
I've also tried to write something like this
Route::group(['middleware' => ['can:earnings,financial_fund']], function () {
Route::get('/payment', [PaymentsController::class, 'getAll']);
Route::post('/payment/cash', [PaymentsController::class, 'addCashPayment']);
});
But this requires both of permissions to exist with the current user.
How can I tell that I want only one of the permissions at least to exist?
I have found that Laravel has introduced the canAny for use in blade templates. Is there a way I can use it in my api.php file while defining the Routes?
I fixed it by creating a new middleware
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
class AuthorizeCanAny
{
public function handle(Request $request, Closure $next, ...$permissions)
{
if (!$request->user()) {
abort(403);
}
$userPermissions = array_map(function ($e) {
return $e['name'];
}, $request->user()->permissions->toArray());
$userPermissionsIntersect = array_intersect($userPermissions, $permissions);
if (!sizeof($userPermissionsIntersect)) {
abort(403);
}
return $next($request);
}
}
Adding the middleware to kernal.php file
protected $routeMiddleware = [
...,
'canAny' => AuthorizeCanAny::class,
];
Then use it in the router
Route::group(['middleware' => ['canAny:earnings,financial_fund']], function () {
Route::get('/payment', [PaymentsController::class, 'getAll']);
Route::post('/payment/cash', [PaymentsController::class, 'addCashPayment']);
});

How to customization redirection in laravel jetstream

i run the laravel jetstream and set to livewire not vue . i usually using auth ui and custom the login in
App -> Http -> controller -> auth -> LoginController
in this LoginController i custom this redirect like this
protected function authenticated(Request $request, $user)
{
if ( $user->isUser() ) {// do your margic here
return redirect()->route('user_dashboard');
}
elseif ($user->isSarpras()) {
return redirect()->route('admin_sarpras_dashboard');
}
}
but on laravel jetstream iam cant found Controller->auth . how to do best manage login and create multiple login using laravel jetstream ?
Found here: https://talltips.novate.co.uk/laravel/laravel-8-conditional-login-redirects
Create a folder under app\Http called Responses
Create a file LoginResponse.php
<?php
namespace App\Http\Responses;
use Illuminate\Support\Facades\Auth;
use Laravel\Fortify\Contracts\LoginResponse as LoginResponseContract;
class LoginResponse implements LoginResponseContract
{
public function toResponse($request)
{
// below is the existing response
// replace this with your own code
// the user can be located with Auth facade
return $request->wantsJson()
? response()->json(['two_factor' => false])
: redirect()->intended(config('fortify.home'));
}
}
Make Laravel use our new Response Class
This new class now replaces the Singleton previously registered by Fortify.
Edit the JetstreamServiceProvider in your app\Providers folder;
In the boot method, add reference to your new response class. When login completes (and the user is actually Authenticated) then your new response will be called.
public function boot()
{
$this->configurePermissions();
Jetstream::deleteUsersUsing(DeleteUser::class);
// register new LoginResponse
$this->app->singleton(
\Laravel\Fortify\Contracts\LoginResponse::class,
\App\Http\Responses\LoginResponse::class
);
}
Two Factor Authentication
If you use 2FA with Jetstream, you will also need to catch the TwoFactorLoginResponse. Use the same approach;
// register new TwofactorLoginResponse
$this->app->singleton(
\Laravel\Fortify\Contracts\TwoFactorLoginResponse::class,
\App\Http\Responses\LoginResponse::class
);
You can return the same response, or create an additional response if you want different behaviour for users that login using 2FA.
Jetstream uses Fortify to power authentication.
Currently, Fortify doesn't have a way to customize redirects.
There is an open issue requesting this bahavior here: https://github.com/laravel/fortify/issues/77
There is a good chance this will be added soon!
Edit
Now you can customize the redirects via config/fortify.php:
'redirects' => [
'login' => 'dashboard',
'logout' => 'home',
'password-confirmation' => null,
'register' => 'dashboard',
'email-verification' => null,
'password-reset' => null,
],
If you need advanced customization of this behavior check out the docs here.

Trying to get property 'name' of non-object after login using laravel passport

hi i have build a login system with laravel passport (personal acces token) so when i want to login and after to redirect to the home page it show me "Trying to get property 'name' of non-object" i know the source of error is when i delete construct " $this->middleware('auth')"
he can't recover the name of authentificated user but when i put the construct the login can't redirect me to home page any solution guys:
this is my currentUser.js:
import axios from "axios";
const state ={
user:{
}
};
const getters= {};
const actions = {
loginUser({},user){
axios.post("/api/login",{
email: user.email,
password: user.password
})
.then(response=>{
console.log(response.data);
if( response.data.acces_token){
//save token mte3na fi locoal storage
localStorage.setItem(
"membre_token",
response.data.acces_token
)
window.location.replace("/home");
}
})
}
};
const mutations={};
export default{
namespaced:true,
state,
getters,
actions,
mutations
}
and this is my web.php:
Route::get('/', function () {
return view('welcome');
});
Route::get('/home', 'HomeController#index')->name('home');
Route::get('/{any}', 'HomeController#index')->where('any','.*');
and this is my home controller:
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
class HomeController extends Controller
{
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
// $this->middleware('auth');
}
/**
* Show the application dashboard.
*
* #return \Illuminate\Contracts\Support\Renderable
*/
public function index()
{
return view('home');
}
}
and this is my login router in api.php
Route::post('/login','API\AuthenController#login');
and this is my login function in AuthenController:
public function login(Request $request){
$validation=$this->validate($request,[
'email' =>'required|email',
'password'=>'required',
]) ;
if(!Auth::attempt($validation)){
return response(['status'=>'error','message'=>'user Undefined']);
}
$accesToken =Auth::user()->createToken('TokenName')->accessToken;
return response(['user'=>Auth::user(), 'acces_token'=>$accesToken]);
}

Prevent role-specific users from accessing route

I have 2 roles, which is admin and user. Now when logging in, the admin goes to the dashboard route while the user goes to home. When user is logged in and changes the url to http://127.0.0.1:8000/dashboard it can access the admin's panel and I don't want that. How can I do achieve this?
PS. I'm new to Laravel
The good practice for this is usage of Middewares.
Create middlewares for admins and users (I'll do that only for admins, you can do that similarly for users):
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Auth;
class AdminMiddleware
{
public function handle($request, Closure $next)
{
if(Auth::check()){
// check auth user role (I don't know how you can implement this for yourself, this is just for me)
if(Auth::user()->role->name == 'admin'){
return $next($request);
} else {
return redirect()->route('admin.dashboard'); // for admins
}
}
return redirect()->route('main'); // for users
}
}
In "app/Http/Kernel.php" in $routeMiddleware array register that (add to end of that array).
'Admin' => \App\Http\Middleware\AdminMiddleware::class,
Now if you are using all requests in "routes/web.php" (actually I think it does), then you can use routes like this for admins:
// USER ROUTES
Route::get('/', 'FrontController#main')->name('main');
// ADMIN ROUTES
Route::group([
'as' => 'admin.',
'middleware' => [ 'Admin' ],
], function () {
Route::get('dashboard', 'AdminController#dashboard');
});
Refresh caches via "php artisan config:cache".
Try it!
Use middleware to admin route or inside the controller
like this:
Route::put('post/{id}', function ($id) {
//
})->middleware('role:editor');
or
Route::middleware(['auth', 'admin'])->group(function (){
Route::get('dashboard', 'HomeController#index')->name('home.index');
});
or inside the controller like this:
public function __construct()
{
$this->middleware(['auth', 'admin'])->except(['index']);
}
or you can use this for middleware roles.

Laravel - Middleware triggered while it should not?

I'm using Laravel 5.4 and Vuejs 2.3 along with VueRouter
I have a website that consists of two parts
example.com
example.com/tools
Problem
If I am at example.com/tools and if I reload the page to the same address example.com/tools the middleware checkUsersPublic is triggered whereas it is only linked to /.
Edit
If I remove { path: '/tools', component: Stats } from VueRouter.js the middleware is not triggered anymore
Question
What should I do change so the middleware is not triggered ?
Routes in Laravel
Route::get('/', 'HomeController#home')->middleware('checkUserPublic')
Route::get('/tools', 'StatsController#home')->middleware('checkUserStats')
Vue router
const routes = [
{ path: '/', component: App },
{ path: '/tools', component: Stats },
];
Middleware checkUsersPublic
class checkUsersPublic
{
public function handle($request, Closure $next)
{
if(auth()->check() && auth()->user()->public_only) {
return $next($request);
}
if(Auth::check()) {Auth::logout();}
return $next($request);
}
}
Instead of doing the middleware in Laravel I did it within vue-router in the beforeEnter guard

Resources