Backend Development

Handling Authorization (Policies & Gates) in Laravel Inertia Pages

Handling Authorization (Policies & Gates) in Laravel Inertia Pages

Authorization is one of the most misunderstood parts of Laravel + Inertia.js applications. In this article we will cover how you can apply policies and gates properly in laravel inertia.

 

 

When dealing with permissions in frontend apps like Vue or React, some of us may do this: 

  • Hide buttons in Vue/React and assume the app is secure ❌
  • Duplicate permission logic in JavaScript ❌
  • Avoid policies entirely and use if checks everywhere ❌

Although this may prevent unintended access to some kind of feature from frontend only, the backend still accessible to anyone. So the proper solution is to prevent this from backend by using laravel authorization features like Polices and Gates.

 

What Are Policies?

Policies are class-based authorization rules tied to a model.

They answer questions like:

  • Can this user update this post?
  • Can this user delete this user?
  • Can this user delete this user?

Example Questions Policies Solve:

Can User A update User B?
Can User A delete Post #42?
Can User A view orders belonging to User B?

 

What Are Gates?

Gates are simple, closure-based authorization rules.

They are best suited for:

  • Feature access
  • Non-model actions
  • Application-level permissions

 

Inertia does not replace Laravel’s authorization system — it simply makes it easier to consume.

 

Creating a Policy

php artisan make:policy UserPolicy --model=User
<?php

namespace App\Policies;

use App\Models\User;

class UserPolicy
{
    public function viewAny(User $user): bool
    {
        return $user->is_admin;
    }

    public function view(User $user, User $model): bool
    {
        return $user->id === $model->id || $user->is_admin;
    }

    public function create(User $user): bool
    {
        return $user->is_admin;
    }

    public function update(User $user, User $model): bool
    {
        return $user->is_admin;
    }

    public function delete(User $user, User $model): bool
    {
        return $user->id === $model->id || $user->is_admin;
    }
}

Policy classes expose the above methods, such as viewAny, view, create, update, etc. These actions typically correspond to the model actions like creating a resource, viewing a resource, updating a resource and so on.

Each of these methods should return a boolean indicating how the user can access a resources based on specific condition. In our example i suppose the user table has the is_admin flag which refers that this user is the admin user.

 

Using Policies in Controllers 

public function edit(User $user)
{
    $this->authorize('update', $user);

    return Inertia::render('Users/Edit', [
        'user' => $user,
    ]);
}

Ensure that the “use AuthorizeRequests” statement is present in the base controller class otherwise laravel will issue an error “call to undefined function authorize()“:

<?php

namespace App\Http\Controllers;

use Illuminate\Foundation\Auth\Access\AuthorizesRequests;

abstract class Controller
{
    use AuthorizesRequests;
}

 

Defining a Gate

Gates defined as a closures that determine if a user is authorized to perform a given action. Typically this will be done in AppServiceProvider:

use Illuminate\Support\Facades\Gate;

public function boot(): void
{
    Gate::define('access-reports', function (User $user) {
       return $user->subscription === 'pro';
   });
}

Using it:

class ReportsController extends Controller
{
    public function show(Request $request, $id): RedirectResponse
    {
        if (! Gate::allows('access-reports')) {
            abort(403);
        }
 
        return redirect('/');
    }
}

 

Exposing Permissions to Inertia

– Option 1: Globally

Inside app/Http/Middleware/HandleInertiaRequests.php

public function share(Request $request): array
    {
        return [
            ...parent::share($request),
            'auth' => [
                'user' => $request->user(),
                'can' => [
                    'manage_users' => $request->user()?->can('viewAny', User::class),
                    'create_users' => $request->user()?->can('create', User::class),
                    'access_reports' => $request->user()?->can('access_reports', Report::class)
                ]
            ],
        ];
    }

Now permissions available globally.

 

– Option 2: Per Page Permissions:
return Inertia::render('Users/Index', [
    'users' => User::paginate(),
    'can' => [
        'create' => Auth::user()->can('create', User::class),
        'delete' => Auth::user()->can('delete', User::class),
    ],
]);

 

Using Permissions in Vue / React

Permissions now available as props to frontend and can be accessed inside Vue or React like so:

Vue Example:

<Link
  v-if="$page.props.auth.can.manage_users"
  href="/users"
>
  Manage Users
</Link>

React Example:

{page.props.auth.can.manage_users && (
  <Link href="/users">Manage Users</Link>
)}

 

Authorization in Forms & Actions

form.delete(route('users.destroy', user.id), {
  onError: () => alert('Unauthorized'),
})

If the policy denies access:

  • Laravel returns 403

  • Inertia handles it automatically

 

Using Laravel packages like spatie/laravel-permission

In large projects usually you will use permissions and roles package, these packages built on top of laravel policies and gates and provides:

  • Roles (admin, editor, customer)

  • Permissions (edit users, delete posts)

  • Database-backed authorization

These packages provides other methods to check for user access like, hasRole(), hasPermission(), etc.

 

0 0 votes
Article Rating

What's your reaction?

Excited
0
Happy
0
Not Sure
0
Confused
0

You may also like

Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments