[!WARNING] This content is AI-generated. Verify before relying on it.

Authorization & Policies

Laravel provides two primary mechanisms for authorizing user actions: Gates (closure-based) and Policies (class-based, organized around Eloquent models).

FeatureBest ForLocation
GatesGeneral actions not tied to a specific model (e.g. view admin panel)Registered in AppServiceProvider or AuthServiceProvider
PoliciesResource actions tied to a model (e.g. view, update, delete a Post)app/Policies/

Generating Policies

# Generate policy mapped to a specific model
php artisan make:policy PostPolicy --model=Post

# Generate a blank policy
php artisan make:policy AdminPolicy

Policies are saved to app/Policies/.


Policy Structure

namespace App\Policies;

use App\Models\Post;
use App\Models\User;
use Illuminate\Auth\Access\Response;

class PostPolicy
{
    /**
     * Perform pre-authorization checks (e.g. grant all permissions to Super Admin).
     */
    public function before(User $user, string $ability): ?bool
    {
        if ($user->is_super_admin) {
            return true;
        }

        return null; // Fall through to individual policy methods
    }

    /**
     * Determine if user can view the list of posts.
     */
    public function viewAny(User $user): bool
    {
        return true;
    }

    /**
     * Determine if user can view a specific post.
     */
    public function view(?User $user, Post $post): bool
    {
        // Type-hinting nullable ?User allows unauthenticated guests
        return $post->is_published || ($user && $user->id === $post->user_id);
    }

    /**
     * Determine if user can create posts.
     */
    public function create(User $user): bool
    {
        return $user->hasVerifiedEmail();
    }

    /**
     * Determine if user can update the post.
     */
    public function update(User $user, Post $post): bool
    {
        return $user->id === $post->user_id;
    }

    /**
     * Determine if user can delete the post.
     */
    public function delete(User $user, Post $post): Response
    {
        return $user->id === $post->user_id
            ? Response::allow()
            : Response::deny('You do not own this post.');
    }
}

[!NOTE] Laravel automatically discovers policies if the policy class is in app/Policies/ and matches the model name (e.g. App\Models\Post -> App\Policies\PostPolicy).


Authorizing Actions

1. In Controllers

Use $this->authorize() (or Gate::authorize()). If unauthorized, Laravel automatically throws an AuthorizationException resulting in an HTTP 403 Forbidden response:

namespace App\Http\Controllers;

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

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

        // Update post...
    }

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

        $post->delete();
    }
}

2. In Form Requests

Authorize directly inside the authorize() method of a Form Request:

public function authorize(): bool
{
    $post = $this->route('post');

    return $this->user()->can('update', $post);
}

3. In Blade Templates

Control UI visibility using @can and @cannot directives:

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

@can('delete', $post)
    <button type="submit">Delete Post</button>
@endcan

@cannot('update', $post)
    <p class="text-muted">You cannot edit this post.</p>
@endcannot

4. In Route Middleware

Protect routes directly at the routing level:

use App\Http\Controllers\PostController;
use Illuminate\Support\Facades\Route;

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

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

Defining & Using Gates

Gates are ideal for standalone actions not tied to a single model:

// Defined in AppServiceProvider.php (boot method)
use App\Models\User;
use Illuminate\Support\Facades\Gate;

Gate::define('access-admin-panel', function (User $user) {
    return $user->is_admin;
});

Using Gates in code:

// Check permission boolean
if (Gate::allows('access-admin-panel')) {
    // Proceed
}

// Check denial
if (Gate::denies('access-admin-panel')) {
    abort(403);
}

// Authorize or abort with 403
Gate::authorize('access-admin-panel');