# How OpenWork Den Implements Multi-Tenancy: JWT Parsing, PostgreSQL RLS, and Tenant Context Propagation

> Discover how OpenWork Den implements multi-tenancy using JWT parsing, PostgreSQL RLS, and tenant context propagation for robust data isolation.

- Repository: [Different AI/openwork](https://github.com/different-ai/openwork)
- Tags: internals
- Published: 2026-07-31

---

**OpenWork Den enforces strict multi-tenancy by extracting organization IDs from JWT tokens, applying PostgreSQL row-level security policies on every query, and propagating tenant context through the EnterpriseDenOrigin gateway to ensure complete data isolation between organizations.**

OpenWork Den, part of the `different-ai/openwork` repository, operates as a single-tenant-per-organization SaaS platform where every request is strictly scoped to a specific organization. The multi-tenancy architecture implements defense-in-depth isolation across three layers: authentication token parsing, database-level row-level security (RLS), and request context propagation. This technical deep-dive examines the complete implementation, from JWT extraction in [`apps/server/src/runtime-db.ts`](https://github.com/different-ai/openwork/blob/main/apps/server/src/runtime-db.ts) to PostgreSQL RLS enforcement.

## Token-Based Tenant Identification

The multi-tenancy enforcement begins at the authentication layer. When a request arrives at the Den API, the server extracts the `Authorization: Bearer <jwt>` header and decodes the organization ID from the token payload.

In [`apps/server/src/runtime-db.ts`](https://github.com/different-ai/openwork/blob/main/apps/server/src/runtime-db.ts), the `decodeOrgIdFromBearer` function validates the JWT signature against the OpenWork public key and extracts the custom `org_id` claim:

```typescript
export function decodeOrgIdFromBearer(bearer: string): string {
  const token = bearer.replace(/^Bearer\s+/i, '');
  const payload = jwt.verify(token, PUBLIC_KEY) as { org_id: string };
  if (!payload.org_id) throw new Error('Missing org_id claim');
  return payload.org_id;
}

```

This function serves as the primary entry point for tenant identification, ensuring that every authenticated request carries a valid organization identifier before proceeding to database operations.

## Database Isolation with Row-Level Security

The Den backend enforces data isolation at the database layer using PostgreSQL Row-Level Security (RLS) policies. Every persistent table that stores user- or organization-specific data—including `capability_credentials`, `workspace_files`, and `session_tokens`—includes a mandatory `org_id` column defined in schemas like [`ee/packages/den-db/src/schema/sharables/capability-credentials.ts`](https://github.com/different-ai/openwork/blob/main/ee/packages/den-db/src/schema/sharables/capability-credentials.ts).

Database migrations, such as [`ee/packages/den-db/drizzle/0019_scim_sso_identity.sql`](https://github.com/different-ai/openwork/blob/main/ee/packages/den-db/drizzle/0019_scim_sso_identity.sql), enable RLS and create policies that automatically filter rows based on the current session variable:

```sql
ALTER TABLE capability_credentials
ADD COLUMN org_id UUID NOT NULL;

ALTER TABLE capability_credentials ENABLE ROW LEVEL SECURITY;
CREATE POLICY org_isolation ON capability_credentials
USING (org_id = current_setting('app.current_org_id')::uuid);

```

The [`runtime-db.ts`](https://github.com/different-ai/openwork/blob/main/runtime-db.ts) module provides the `withTenant` helper to set this session variable before executing queries:

```typescript
export async function withTenant<T>(orgId: string, fn: () => Promise<T>) {
  await db.execute(sql`SET LOCAL app.current_org_id = ${orgId}`);
  return fn();
}

```

All repository queries wrap database operations inside `withTenant`, guaranteeing that SQL queries cannot cross tenant boundaries even if application logic contains bugs.

## Request Context Propagation via EnterpriseDenOrigin

All Den-specific routes flow through the `EnterpriseDenOrigin` class in [`apps/server/src/enterprise-den-origin.ts`](https://github.com/different-ai/openwork/blob/main/apps/server/src/enterprise-den-origin.ts), which acts as the central tenant gateway. This class constructs a `TenantContext` object containing the `orgId` and optional `tenantId` (for multi-tenant MCP connections), then injects this context into downstream services.

```typescript
export class EnterpriseDenOrigin {
  async handle(req: Request) {
    const orgId = decodeOrgIdFromBearer(req.headers.get('authorization')!);
    await withTenant(orgId, async () => {
      const ctx = new TenantContext(orgId, req);
      // Route dispatch using ctx
    });
  }
}

```

This centralized approach ensures that workspace handlers, credential managers, and MCP connectors receive the correct tenant context without manual propagation through every function call.

## Multi-Tenant MCP Connection Management

For integrations with external multi-tenant providers like Microsoft Entra, Den maintains mappings between OpenWork organization IDs and remote tenant IDs. The `ManagedProviderAuth` layer in [`apps/server/src/managed-provider-auth.ts`](https://github.com/different-ai/openwork/blob/main/apps/server/src/managed-provider-auth.ts) generates tenant-scoped OAuth URLs using the stored `mcp_tenant_id`:

```typescript
export function makeAuthorizeUrl(tenantId: string, clientId: string) {
  return `${BASE_URL}/entra/${tenantId}/oauth2/v2.0/authorize?client_id=${clientId}`;
}

```

When creating MCP connections, the system stores both the OpenWork `orgId` and the remote `mcp_tenant_id`, ensuring OAuth flows target the correct external tenant while maintaining isolation within the Den database.

## Organization-Specific Configuration and Feature Gating

Per-organization feature flags and configuration settings are managed through the `runtime-config` store in [`apps/server/src/runtime-config.ts`](https://github.com/different-ai/openwork/blob/main/apps/server/src/runtime-config.ts). The `getOrgConfig` function merges global defaults with organization-specific overrides retrieved from the `org_config` table:

```typescript
export async function getOrgConfig(orgId: string) {
  const global = await loadGlobalConfig();
  const orgOverrides = await db.select().from(org_config).where(eq(org_config.org_id, orgId));
  return { ...global, ...orgOverrides };
}

```

This pattern prevents organizations from accessing features they have not purchased, such as MCP connections, by checking flags like `enableMcp` before executing restricted operations.

## Practical Implementation Examples

The following examples demonstrate common multi-tenancy patterns in the Den codebase.

**Revoking Credentials with Tenant Scoping**

The credential revocation handler in [`ee/apps/den-api/src/credential-revocation.ts`](https://github.com/different-ai/openwork/blob/main/ee/apps/den-api/src/credential-revocation.ts) shows how database operations automatically respect tenant boundaries:

```typescript
export async function revokeCredential(req: Request) {
  const orgId = decodeOrgIdFromBearer(req.headers.get('authorization')!);
  return withTenant(orgId, async () => {
    await db.delete(credentialTable).where(eq(credentialTable.org_id, orgId));
    return { success: true };
  });
}

```

**Querying with Row-Level Security**

Custom queries in repository layers use `withTenant` to ensure RLS policies apply:

```typescript
export async function listCredentials(orgId: string) {
  return withTenant(orgId, async () => {
    return db.select().from(capability_credentials).all();
  });
}

```

**Feature Flag Enforcement**

Before creating MCP connections, the system validates organizational entitlements:

```typescript
export async function canCreateMcpConnection(orgId: string) {
  const cfg = await getOrgConfig(orgId);
  if (!cfg.enableMcp) throw new Error('MCP connections disabled for this org');
}

```

## Summary

OpenWork Den implements multi-tenancy through a defense-in-depth strategy that combines application-level and database-level controls:

- **JWT Extraction**: The `decodeOrgIdFromBearer` function in [`apps/server/src/runtime-db.ts`](https://github.com/different-ai/openwork/blob/main/apps/server/src/runtime-db.ts) extracts organization IDs from authentication tokens
- **PostgreSQL RLS**: Row-level security policies in migrations like [`ee/packages/den-db/drizzle/0019_scim_sso_identity.sql`](https://github.com/different-ai/openwork/blob/main/ee/packages/den-db/drizzle/0019_scim_sso_identity.sql) enforce data isolation at the database layer
- **Context Propagation**: The `EnterpriseDenOrigin` class centralizes tenant context creation and injection for all Den routes
- **Session Scoping**: The `withTenant` helper sets PostgreSQL session variables to ensure queries cannot cross tenant boundaries
- **Feature Gating**: The `runtime-config` system enforces organization-specific feature access and plan limits

## Frequently Asked Questions

### How does OpenWork Den prevent cross-tenant data leakage?

OpenWork Den prevents cross-tenant data leakage through PostgreSQL Row-Level Security (RLS) policies that automatically filter rows based on the `app.current_org_id` session variable. Even if application code accidentally omits a `WHERE org_id` clause, the database layer will only return rows matching the current session's organization ID. Additionally, the `withTenant` wrapper in [`apps/server/src/runtime-db.ts`](https://github.com/different-ai/openwork/blob/main/apps/server/src/runtime-db.ts) sets this session variable for every request, creating a database-level safety net.

### What is the role of the EnterpriseDenOrigin class in multi-tenancy?

The `EnterpriseDenOrigin` class in [`apps/server/src/enterprise-den-origin.ts`](https://github.com/different-ai/openwork/blob/main/apps/server/src/enterprise-den-origin.ts) serves as the central gateway for all Den-specific API routes. It extracts the organization ID from the JWT token using `decodeOrgIdFromBearer`, initializes the tenant context, and wraps all downstream operations in a `withTenant` block. This centralized approach ensures that every handler receives the correct tenant context without requiring manual token parsing in individual route handlers.

### How are multi-tenant MCP connections handled differently from standard connections?

Multi-tenant MCP connections store two identifiers: the OpenWork organization ID (`orgId`) and the external provider's tenant ID (`mcp_tenant_id`). When generating OAuth URLs in [`apps/server/src/managed-provider-auth.ts`](https://github.com/different-ai/openwork/blob/main/apps/server/src/managed-provider-auth.ts), the system uses the remote `tenantId` to construct provider-specific endpoints (e.g., `/entra/{tenantId}/oauth2/v2.0/authorize`), ensuring the OAuth flow targets the correct external tenant while the Den database records maintain strict isolation through the `org_id` column.

### Where is the tenant context initialized in the request lifecycle?

The tenant context initializes at the entry point of the Den API routing layer in [`apps/server/src/enterprise-den-origin.ts`](https://github.com/different-ai/openwork/blob/main/apps/server/src/enterprise-den-origin.ts). When a request arrives, the `handle` method immediately extracts the bearer token, decodes the `org_id` claim, creates a `TenantContext` instance, and invokes `withTenant` to set the PostgreSQL session variable before any database queries execute. This ensures the tenant context is established before any service logic runs.