Nguyên tắc: token phải trả lời được "ai, thuộc tenant nào, được làm gì", và API không bao giờ lấy tenant từ tham số do client gửi lên.
Đặt tenant_id vào token, không lấy từ request. Sai lầm kinh điển là GET /api/orders?tenantId=... rồi tin vào query string — kẻ tấn công chỉ cần đổi số là đọc dữ liệu tenant khác (IDOR).
json
{
"sub": "user_123",
"tenant_id": "acme",
"scope": "orders:read orders:write",
"roles": ["manager"],
"aud": "api://orders"
}Scope và role không thay thế nhau. scope giới hạn app được phép làm gì thay mặt user (do OAuth cấp), còn role/permission là quyền của chính user trong hệ thống. Kiểm tra phải là giao của cả hai: token có orders:write và user có quyền ghi ở tenant đó.
Một user thuộc nhiều tenant — hai hướng:
- Phát token riêng cho từng tenant (đổi tenant = lấy token mới). Token gọn, sạch, dễ audit.
- Nhét mảng
tenantsvào token rồi chọn theo header. Token phình lên và mọi API phải tự chọn đúng phần tử — dễ sai. Chỉ dùng khi số tenant nhỏ.
Hai điểm nữa nên nói:
- Đừng nhét quá nhiều permission vào JWT. Token to lên theo header mọi request, và quyền vừa bị thu hồi vẫn còn hiệu lực tới khi hết hạn. Quyền chi tiết nên tra ở server; token chỉ mang định danh và scope thô.
- Áp ràng buộc tenant ở tầng dữ liệu (điều kiện
tenant_id = ?bắt buộc, hoặc Row Level Security) chứ đừng chỉ dựa vào kiểm tra ở controller — quên một chỗ là rò dữ liệu chéo tenant.