Back to Read More
LaravelPHP

Laravel Authorization

Dec 1, 2025

Authentication answers "Who are you?" β€” Authorization answers "What are you allowed to do?" Laravel provides two ways to authorize actions: Gates (simple closures) and Policies (organized by model). Think of Gates for quick checks and Policies for model-specific permissions.

1. Gates

Gates are simple closures that determine if a user can perform an action. Define them in AppServiceProvider.

app/Providers/AppServiceProvider.php
use Illuminate\Support\Facades\Gate;
use App\Models\User;
use App\Models\Post;

public function boot(): void
{
    // Simple gate: only admins can access
    Gate::define('access-dashboard', function (User $user) {
        return $user->role === 'admin';
    });

    // Gate with model: only the author can update a post
    Gate::define('update-post', function (User $user, Post $post) {
        return $user->id === $post->user_id;
    });

    // Gate: admin can do everything (before hook)
    Gate::before(function (User $user, string $ability) {
        if ($user->role === 'admin') {
            return true; // Admin bypasses all gates
        }
    });
}
Using Gates
use Illuminate\Support\Facades\Gate;

// In controller
public function edit(Post $post)
{
    // Check permission β€” returns 403 if denied
    Gate::authorize('update-post', $post);

    return view('posts.edit', compact('post'));
}

// Check without throwing error
if (Gate::allows('update-post', $post)) {
    // User can update
}

if (Gate::denies('update-post', $post)) {
    // User cannot update
}

// In Blade templates
@can('update-post', $post)
    <a href="/posts/${'{{'} $post->id ${'}}'}/edit">Edit</a>
@endcan

@cannot('update-post', $post)
    <p>You cannot edit this post.</p>
@endcannot

2. Policies

Policies organize authorization logic around a specific model. Each method in a policy corresponds to an action (view, create, update, delete).

Terminal
# Generate a policy for Post model
php artisan make:policy PostPolicy --model=Post
app/Policies/PostPolicy.php
namespace App\Policies;

use App\Models\Post;
use App\Models\User;

class PostPolicy
{
    // Can the user view any posts?
    public function viewAny(User $user): bool
    {
        return true; // Everyone can view posts list
    }

    // Can the user view this specific post?
    public function view(User $user, Post $post): bool
    {
        return true; // Everyone can view a post
    }

    // Can the user create posts?
    public function create(User $user): bool
    {
        return $user->role === 'admin' || $user->role === 'author';
    }

    // Can the user update this post?
    public function update(User $user, Post $post): bool
    {
        return $user->id === $post->user_id;
    }

    // Can the user delete this post?
    public function delete(User $user, Post $post): bool
    {
        return $user->id === $post->user_id;
    }
}
Auto-discovery: Laravel automatically discovers policies if you follow naming conventions: Post model β†’ PostPolicy. No manual registration needed.

3. Using Policies

In Controllers

app/Http/Controllers/PostController.php
class PostController extends Controller
{
    public function index()
    {
        $this->authorize('viewAny', Post::class);
        $posts = Post::all();
        return view('posts.index', compact('posts'));
    }

    public function edit(Post $post)
    {
        // Throws 403 if user is not the author
        $this->authorize('update', $post);
        return view('posts.edit', compact('post'));
    }

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

        $post->update($request->validated());
        return redirect()->route('posts.show', $post);
    }

    public function destroy(Post $post)
    {
        $this->authorize('delete', $post);

        $post->delete();
        return redirect()->route('posts.index');
    }
}

Via User Model

Using on User model
$user = Auth::user();

if ($user->can('update', $post)) {
    // User can update this post
}

if ($user->cannot('delete', $post)) {
    // User cannot delete this post
}

In Blade Templates

Blade
@can('create', App\Models\Post::class)
    <a href="/posts/create">Create Post</a>
@endcan

@can('update', $post)
    <a href="/posts/${'{{'} $post->id ${'}}'}/edit">Edit</a>
@endcan

@can('delete', $post)
    <form method="POST" action="/posts/${'{{'} $post->id ${'}}'}">
        @csrf
        @method('DELETE')
        <button>Delete</button>
    </form>
@endcan

4. Authorization in Routes

routes/web.php
// Using 'can' middleware
Route::put('/posts/{post}', [PostController::class, 'update'])
    ->middleware('can:update,post');

Route::delete('/posts/{post}', [PostController::class, 'destroy'])
    ->middleware('can:delete,post');

// For actions without model instance
Route::get('/posts/create', [PostController::class, 'create'])
    ->middleware('can:create,App\Models\Post');

5. Simple Role-Based Access

A practical example combining Gates for role-based access:

Migration: add role to users
// Add to users migration
$table->string('role')->default('user'); // user, author, admin
AppServiceProvider β€” define role gates
Gate::define('admin', fn (User $user) => $user->role === 'admin');
Gate::define('author', fn (User $user) => in_array($user->role, ['author', 'admin']));
Gate::define('manage-users', fn (User $user) => $user->role === 'admin');
Usage
// In routes
Route::middleware('can:admin')->group(function () {
    Route::get('/admin', [AdminController::class, 'index']);
    Route::resource('/users', UserController::class);
});

// In Blade
@can('admin')
    <a href="/admin">Admin Panel</a>
@endcan

Summary

  • βœ“Gates β€” simple closure-based authorization checks
  • βœ“Policies β€” model-specific authorization organized by actions
  • βœ“$this->authorize() β€” check in controllers (throws 403)
  • βœ“@can / @cannot β€” conditional rendering in Blade
  • βœ“can: middleware β€” protect routes with authorization

Β© 2026 Koeuk KOS. All rights reserved.

Built with Nuxt.js, Vue.js & Tailwind CSS