Tenancy and security
Customer Portal is organization-scoped. A signed-in user can belong to multiple organizations, but each request operates in the context of one active organization.
Role model
The portal distinguishes two levels of authority:
- System administrator — manages the whole portal and its organizations.
- Organization role —
owner,admin, ormemberwithin one organization.
Feature policies map organization roles to feature-specific actions. A system administrator receives the standard platform bypass.
Tenant-scoped APIs
Feature handlers should start with requireFeatureAccess:
import { requireFeatureAccess } from '@nuxt-customer-portal/core/server'
import { exampleFeature } from '../../../shared/feature'
export default defineEventHandler(async (event) => {
const { session, organizationId } = await requireFeatureAccess(
event,
exampleFeature.policy,
'read'
)
// Query using organizationId from the authenticated context.
})
Never trust an organization ID from a request body or query string when it can be derived from the session. Every tenant-owned query must include the resolved organizationId.
Database separation
Each feature owns a PostgreSQL schema. This reduces naming collisions and makes data ownership visible, but schema separation alone is not a tenancy boundary: rows must still be scoped to the active organization.
Cross-schema foreign keys to core organizations and users are expected. Feature schemas should use deletion behavior deliberately so that organization cleanup cannot leave invalid records.
Client-side visibility
Audience-aware navigation improves the interface, but it does not secure data. Pages and buttons may be hidden for an unauthorized role; the corresponding server route must independently reject the request.
Contribution checks
Security-sensitive changes should include tests for:
- unauthenticated access;
- insufficient organization roles;
- access with the wrong active organization;
- system-administrator behavior;
- destructive operations and their dependent records.