Skip to content

Authorize every entrypoint

HTTP, Job, and Command boundaries authorize before their single UseCase call. A Policy answers whether an actor may act; it does not replace Domain invariants or tenant isolation.

Before you begin

Run composer run reference:test:authorization and read authorization.

Restore context before a Job call

examples/reference-app/app/Pulsar/Services/Internal/Modules/Orders/Jobs/ConfirmOrderJob.php — Actor and tenant reconstruction before Policy authorization.

php
<?php

namespace App\Pulsar\Services\Internal\Modules\Orders\Jobs;

use App\Models\User;
use App\Pulsar\Domain\Orders\Models\Order;
use App\Pulsar\Services\Internal\Modules\Orders\UseCases\ConfirmPendingOrder;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Gate;

final class ConfirmOrderJob implements ShouldQueue
{
    use Queueable;

    public int $tries = 3;

    public function backoff(): array
    {
        return [10, 30, 60];
    }

    public function __construct(
        public readonly int $orderId,
        public readonly int $actorId,
        public readonly string $tenantId,
        public readonly string $idempotencyKey,
    ) {}

    // #region confirm-order-job
    public function handle(ConfirmPendingOrder $useCase): void
    {
        $actor = User::query()->findOrFail($this->actorId);
        $order = Order::query()->findOrFail($this->orderId);
        abort_unless($actor->tenant_id === $this->tenantId && $order->tenant_id === $this->tenantId, 403);
        Gate::forUser($actor)->authorize('confirm', $order);

        $useCase->execute($order->id, $this->idempotencyKey);
    }
    // #endregion
}
HTTP, Job, Command, and Listener audiences establish or reconstruct actor and tenant data, authorize an ability through a Gate or Policy, scope a subject to the tenant, call one UseCase, reject an invariant or mismatch, and reuse retry idempotency.
Enforce authorization and tenant isolation independently. Permission still needs tenant scope and valid Domain state; a retry still needs idempotency.

Interpretation. Policy denial and tenant mismatch are independent rejection paths.

Verify and troubleshoot

The HTTP and queue tests exercise authorization denials. Do not put authorization only in a Form Request; non-HTTP entrypoints do not have one.