Deploy 2: 2FA (App + Email) + REST API + CSV import-export + auto backup
- Filament v5 multiFactorAuthentication enabled on both panels (App + Email) - HasAppAuthentication + HasEmailAuthentication on User and SuperAdmin - Migration: app_authentication_secret + recovery_codes + email_authentication_at - Sanctum REST API: /api/v1/login, /me, clients, vehicles, work-orders - EnsureTokenMatchesTenant middleware blocks cross-tenant token usage - CsvImportExport service: clients + vehicles bulk via plain CSV - Import/Export buttons on Client + Vehicle list pages - ApiTokens page in tenant panel (generate/revoke + last-used) - BackupAllTenantsCommand + scheduler (daily 03:00, retain 14 days) - Background scheduler in entrypoint.sh
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use App\Tenancy\TenantManager;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
|
||||
|
||||
/**
|
||||
* Defense in depth: even though Sanctum auth + TenantScope should isolate
|
||||
* tenants, this middleware EXPLICITLY rejects any request where the
|
||||
* authenticated user's company_id does not match the resolved tenant.
|
||||
*
|
||||
* Stops the entire class of "stolen token used on wrong subdomain" attacks.
|
||||
*/
|
||||
class EnsureTokenMatchesTenant
|
||||
{
|
||||
public function handle(Request $request, Closure $next)
|
||||
{
|
||||
$user = $request->user();
|
||||
$tenant = app(TenantManager::class)->current();
|
||||
|
||||
if (! $user || ! $tenant) {
|
||||
throw new AccessDeniedHttpException('Tenant context required.');
|
||||
}
|
||||
|
||||
if ($user->company_id !== $tenant->id) {
|
||||
throw new AccessDeniedHttpException('Token does not belong to this tenant.');
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user