← Back to Laravel SaaS foundation team

Skill · Version 1.0.0

Tenant context propagation

Establishes verified workspace context once and carries it explicitly through application boundaries without unsafe default fallbacks.

tenant-context-propagation

Team members using this skill

See how it helps

When is it useful?

Use it when tenant identity passes between HTTP handlers, jobs, realtime channels or command-line operations.

What does it help you do?

  • Separates authentication, membership and resource authorization.
  • Rejects missing or unverified workspace context without default fallback.
  • Carries immutable context through requests, jobs and realtime connections.

Try asking this

Trace workspace identity from HTTP request through a queued export worker. Queue an export from workspace A while the user switches to B. The worker must retain verified A context or fail explicitly, never inherit the new selection.

Detailed documentation

Read the skill’s original instructions, examples and checks in their authored language.

Details…Show less

Tenant Context Propagation

When to Use

Use when designing middleware, request context stores, repository layers, background job dispatchers, WebSocket handlers, or CLI commands in a multi-tenant application.

Core Principle: Separate Identity, Membership, and Permission

Every tenant-scoped operation must verify three distinct questions in order:

  1. Authentication (Who is the actor?): Validated userId, service principal, or signed webhook identity.
  2. Workspace Membership (Does this actor belong to tenantId?): Verified active membership in workspace_memberships for the requested tenantId (and verified workspace state is active).
  3. Action & Resource Authorization (Can they perform this action on this resource?): Role/permission check plus verification that the target resource's tenant_id equals the active tenantId.

Fail-Closed Context Resolution

1. Never Trust Unverified Client Tenant Hints or Fallback Defaults
  • A subdomain, URL parameter (/workspaces/:workspaceId), header (X-Workspace-Id), or body field is only a candidate hint until the server verifies active membership for the authenticated actor.
  • Fail-Closed Rule: If tenantId is missing, malformed, or unverified when a tenant-scoped repository, cache, or storage method is called, throw an explicit authorization/context error immediately.
  • Never fall back to a "default workspace," "first workspace of user," or omit the WHERE tenant_id = ? filter when tenantId is null or undefined! (Watch out for ORMs where { where: { tenantId: undefined } } silently strips the WHERE clause and returns all tenants' rows!)
2. Scoped Execution Context (AsyncLocalStorage / contextvars)
  • Never store per-request currentTenantId on a singleton service instance, module-level global variable, or shared DB client property—concurrent requests will overwrite each other's tenant context!
  • Pass an explicit, immutable TenantContext ({ tenantId, actorId, actorType, membershipRole, requestId }) or bind it strictly to the async execution tree (AsyncLocalStorage in Node.js, contextvars in Python, context.Context in Go, request-scoped container in PHP/Laravel).
3. Distinguish Tenant-Scoped Calls from Explicit System Scopes

Legitimate cross-tenant operations (e.g., billing reconciliation cron, system migrations, global health metrics) must not use a backdoor tenantId = null:

  • Require an explicit SystemExecutionContext (scope: 'system', reason, actor) via a dedicated entry point so accidental omission of tenant context in user code still fails closed.
4. Rehydrate & Re-Verify Context in Async & Long-Lived Workers

When work crosses an execution boundary (BullMQ/SQS/RabbitMQ job, scheduled task, event listener, WebSocket message):

  1. At Enqueue Time: Serialize { tenantId, actorId, actorType, enqueuedAt } into the job envelope.
  2. At Worker Execution Time:
    • Initialize a fresh, isolated context scope for that single job and clear it in finally.
    • Re-verify that the workspace is still active (and, for user-delegated delayed actions, that the user's membership and permission have not been revoked since enqueue time) before executing mutations.

Included files 1

  • SKILL.md