Authorization in Laravel

Documentation: https://laravel.com/docs/11.x/authorization#gates

Authorization with a Gate

Let's say we want to prevent authenticated users from accessing invoices for other users. A user should only be allowed to view their invoices but not anyone else's. To achieve this, we can define a Gate:

app/Providers/AuthServiceProvider.php
// inside of the boot method
Gate::define('view-invoice', function (User $user, Invoice $invoice) {
  return $user->email === $invoice->customer->Email;
});

Then, in our controller, we can use several different approaches to utilize our Gate:

app/Http/Controllers/InvoiceController.php
class InvoiceController extends Controller
{
  public function show($id)
  {
    $invoice = Invoice::with([
      'invoiceItems.track',
      'invoiceItems.track.album',
      'invoiceItems.track.album.artist',
    ])->find($id);

    // Approach 1
    if (Gate::denies('view-invoice', $invoice)) {
      abort(404);
    }

    // Approach 2
    if (!Gate::allows('view-invoice', $invoice)) {
      abort(404);
    }

    // Approach 3
    if (Auth::user()->cannot('view-invoice', $invoice)) {
      abort(404);
    }

    // Approach 4
    if (!Auth::user()->can('view-invoice', $invoice)) {
      abort(404);
    }

    return view('invoice.show', [
      'invoice' => $invoice,
    ]);
  }
}

We can also access our Gate in a template using the @can or @cannot directives:

@can('view-invoice', $invoice)
  <a href="{{ route('invoice.show', [ 'id' => $invoice->id ]) }}">
    View invoice
  </a>
@endcan
@cannot('view-invoice', $invoice)
  <p>You don't have access to this invoice.</p>
@endcan

Authorization with a Policy

We can also perform authorization checks via Policies. Policies are classes that organize authorization logic around a particular model.

php artisan make:policy InvoicePolicy --model=Invoice
app/Policies/InvoicePolicy.php
namespace App\Policies;

use App\Models\Invoice;
use App\Models\User;
use Illuminate\Auth\Access\HandlesAuthorization;

class InvoicePolicy
{
  public function view(User $user, Invoice $invoice)
  {
    return $user->email === $invoice->customer->Email;
  }
}
app/Http/Controllers/InvoiceController.php
class InvoiceController extends Controller
{
  public function show($id)
  {
    $invoice = Invoice::with([
      'invoiceItems.track',
      'invoiceItems.track.album',
      'invoiceItems.track.album.artist',
    ])->find($id);

    // Approach 1
    if (Auth::user()->cannot('view', $invoice)) {
      abort(404);
    }

    // Approach 2
    if (!Auth::user()->can('view', $invoice)) {
      abort(404);
    }

    return view('invoice.show', [
      'invoice' => $invoice,
    ]);
  }
}

Similar to before, we can perform authorization checks in a template via a Policy using the @can or @cannot directives:

@can('view', $invoice)
  <a href="{{ route('invoice.show', [ 'id' => $invoice->id ]) }}">
    View invoice
  </a>
@endcan
@cannot('view', $invoice)
  <p>You don't have access to this invoice.</p>
@endcan

Documentation: https://laravel.com/docs/11.x/authorization#writing-policies