Laravel Authorization and Policies

Lesson 32 of 50 - Learn how to control what authenticated users are allowed to do in a Laravel application.

64%
1. What Is Authorization?

Authorization determines what an authenticated user is allowed to access or perform in an application.

Example in a school management system:

  • An administrator can add, edit, and delete students.
  • A student can view their own profile.
  • A student cannot delete another student's record.
  • A teacher can manage assignments for their classes.

Authentication vs Authorization:

Authentication Authorization
Checks who the user is. Checks what the user can do.
Example: Login with email and password. Example: Allow only admins to delete students.
Commonly uses authentication services. Uses gates, policies, middleware, or role permissions.

Authentication and authorization work together to protect application resources.

2. Authorization Using Gates

A gate is a simple authorization rule. Gates are useful when permission logic does not belong to a particular model, such as checking whether a user can access an administration dashboard.

In modern Laravel applications, define gates in AppServiceProvider.php or an appropriate authorization provider.

Example:

<?php

namespace App\Providers;

use App\Models\User;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Gate::define('access-admin', function (User $user) {
            return $user->role === 'admin';
        });
    }
}

This example assumes your users table contains a role column.

Check the permission in a controller:

use Illuminate\Support\Facades\Gate;

if (Gate::allows('access-admin')) {
    // User is authorized.
} else {
    abort(403, 'Unauthorized action.');
}

A 403 response means the user is not permitted to perform the requested action.

3. Authorization Using can() and cannot()

Laravel provides convenient methods for checking authorization permissions.

Check whether a user can access the admin area:

if (auth()->user()->can('access-admin')) {
    echo 'Welcome to the admin area';
} else {
    echo 'Access denied';
}

Check whether a user cannot access it:

if (auth()->user()->cannot('access-admin')) {
    abort(403, 'You cannot access this page.');
}

Use authorization in Blade:

@can('access-admin')
    <a href="/admin">Admin Dashboard</a>
@endcan

@cannot('access-admin')
    <p>You do not have admin access.</p>
@endcannot

Hiding a link in Blade improves the user interface, but it does not secure the route. Always enforce authorization on the server as well.

4. What Is a Laravel Policy?

A policy organizes authorization rules for a particular model. For example, a StudentPolicy can define who may view, update, or delete student records.

Create a policy:

php artisan make:policy StudentPolicy --model=Student

This creates app/Policies/StudentPolicy.php.

Example StudentPolicy:

<?php

namespace App\Policies;

use App\Models\Student;
use App\Models\User;

class StudentPolicy
{
    public function viewAny(User $user): bool
    {
        return $user->role === 'admin';
    }

    public function view(User $user, Student $student): bool
    {
        return $user->role === 'admin'
            || $user->student_id === $student->id;
    }

    public function update(User $user, Student $student): bool
    {
        return $user->role === 'admin';
    }

    public function delete(User $user, Student $student): bool
    {
        return $user->role === 'admin';
    }
}

This is an example role-based policy. It assumes the users table has a role field and a student_id field for linking a student account to its student record.

Adapt the field names and ownership logic to your database schema. Policies for sensitive records should be tested carefully.

5. Use a Policy in a Controller

You can authorize actions inside a controller using the authorize() method.

Example StudentController:

<?php

namespace App\Http\Controllers;

use App\Models\Student;
use Illuminate\Http\Request;

class StudentController extends Controller
{
    public function index()
    {
        $this->authorize('viewAny', Student::class);

        $students = Student::paginate(10);

        return view('students.index', compact('students'));
    }

    public function edit(Student $student)
    {
        $this->authorize('update', $student);

        return view('students.edit', compact('student'));
    }

    public function update(Request $request, Student $student)
    {
        $this->authorize('update', $student);

        $validated = $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'email', 'max:255'],
        ]);

        $student->update($validated);

        return redirect()
            ->route('students.index')
            ->with('success', 'Student updated successfully.');
    }

    public function destroy(Student $student)
    {
        $this->authorize('delete', $student);

        $student->delete();

        return redirect()
            ->route('students.index')
            ->with('success', 'Student deleted successfully.');
    }
}

The authorize() method checks the corresponding policy and throws an authorization exception when access is denied.

This example assumes the Student model supports mass assignment for the validated fields and that the named route and views exist.

6. Protect Routes Using Middleware

Middleware can protect routes based on authentication or other access requirements.

Protect a route with authentication:

use Illuminate\Support\Facades\Route;

Route::get('/dashboard', function () {
    return view('dashboard');
})->middleware('auth');

Authentication middleware ensures the user is logged in. It does not automatically make the user an administrator.

Protect a route using a Gate:

Route::get('/admin', function () {
    return view('admin.dashboard');
})->middleware(['auth', 'can:access-admin']);

This route requires both authentication and permission to access the admin area.

For Laravel 11 and later, custom middleware aliases are typically configured in bootstrap/app.php. Laravel 10 commonly configures aliases in app/Http/Kernel.php.

7. Create Role-Based Middleware

If your application has roles such as admin, teacher, and student, custom middleware can check a user's role.

Create the middleware:

php artisan make:middleware RoleMiddleware

Open app/Http/Middleware/RoleMiddleware.php.

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class RoleMiddleware
{
    public function handle(
        Request $request,
        Closure $next,
        string $role
    ): Response {
        $user = $request->user();

        if (! $user || $user->role !== $role) {
            abort(403, 'You are not authorized to access this page.');
        }

        return $next($request);
    }
}

This example assumes the authenticated user has a role column.

Register the middleware alias:

In Laravel 11 and later, add the alias inside the existing withMiddleware() configuration in bootstrap/app.php:

use App\Http\Middleware\RoleMiddleware;
use Illuminate\Foundation\Configuration\Middleware;

->withMiddleware(function (Middleware $middleware): void {
    $middleware->alias([
        'role' => RoleMiddleware::class,
    ]);
})

Merge this alias into your existing configuration rather than creating a second withMiddleware() call.

Use the middleware on a route:

Route::get('/admin', function () {
    return view('admin.dashboard');
})->middleware(['auth', 'role:admin']);

For Laravel 10, register the alias in the $middlewareAliases array in app/Http/Kernel.php.

8. Authorization with Blade and Policies

Policies can also control which actions appear in your Blade views.

Show the Edit button only when authorized:

@can('update', $student)
    <a href="{{ route('students.edit', $student->id) }}"
       class="btn btn-primary">
        Edit Student
    </a>
@endcan

Show the Delete button only when authorized:

@can('delete', $student)
    <form
        action="{{ route('students.destroy', $student->id) }}"
        method="POST"
    >
        @csrf
        @method('DELETE')

        <button type="submit" class="btn btn-danger">
            Delete Student
        </button>
    </form>
@endcan

Check authorization in a controller:

if (auth()->user()->cannot('update', $student)) {
    abort(403);
}

Blade directives control the interface, while controller authorization checks enforce the permission on the server. Use both when appropriate.

9. Practical Assignment

Build an authorization system for a student management application.

  1. Add a role column to your users table.
  2. Create admin and student user accounts.
  3. Create a Gate for accessing the admin dashboard.
  4. Create a StudentPolicy.
  5. Allow admins to view all student records.
  6. Allow students to view only their own profile.
  7. Allow only admins to edit or delete student records.
  8. Protect the admin route with authentication and authorization.
  9. Create role-based middleware.
  10. Use Blade authorization directives to display permitted actions.
  11. Test unauthorized access and confirm it returns a 403 response.

Challenge:

Extend the application with three roles: administrator, teacher, and student. Administrators manage all records, teachers manage permitted assignments, and students access only their own profile and assignments.

Never rely only on hiding buttons or menu links. Enforce authorization on every protected server-side operation and verify ownership of individual records.

Quick Quiz

1. What does authorization determine?




2. Which Laravel feature organizes permissions for a model?




3. Which middleware commonly requires a logged-in user?