<?php
// auth/middleware.php: include after session_start()
function jsonError(int $status, string $message): never {
    http_response_code($status);
    header('Content-Type: application/json');
    exit(json_encode(['error' => $message]));
}

function requireAuth(): array {
    if (empty($_SESSION['user_id'])) {
        jsonError(401, 'Not signed in');
    }
    return ['id' => (int) $_SESSION['user_id'], 'role' => (string) $_SESSION['role']];
}

function requireRole(string ...$roles): array {
    $user = requireAuth();
    if (!in_array($user['role'], $roles, true)) {
        jsonError(403, 'Not allowed');
    }
    return $user;
}

// api/users.php
$current = requireRole('admin');
// api/invoices.php
$current = requireRole('admin', 'supervisor', 'collaborator');

The never return type (PHP 8.1+) tells static analysers that the function always exits. On older PHP, remove it.