# How Multi-Organization Delegated Auth Works in OpenSEO: A Technical Deep Dive

> Learn how OpenSEO's multi-organization delegated auth works. Discover how user-specific organizations ensure isolated workspaces for secure, separate hosted organizations.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: deep-dive
- Published: 2026-07-21

---

**OpenSEO implements multi-organization delegated authentication by creating a deterministic, user-specific organization (format `delegated-${userId}`) for each delegated auth session, allowing users to maintain separate hosted organizations while ensuring every delegated request operates within an isolated workspace.**

OpenSEO's architecture supports both hosted authentication and delegated authentication modes (such as Cloudflare Access or local development) within a single codebase. Understanding how multi-organization delegated auth works in OpenSEO requires examining the middleware layer that resolves user contexts and the repository pattern that maintains organization isolation. This implementation ensures that users authenticating via delegated methods receive a dedicated organization while preserving their ability to belong to multiple organizations across different authentication modes.

## Core Architecture of Delegated Authentication

### The Delegated Organization Pattern

When a user authenticates via a delegated provider, the system cannot rely on pre-existing organization memberships from hosted auth. Instead, OpenSEO generates a unique **organization identifier** using the pattern `delegated-${userId}`. This deterministic ID ensures consistency across requests while creating a sandboxed environment for the user's data.

### User Record Guarantee

Before creating the delegated organization, the middleware ensures a valid user record exists in the database. The `ensureUserRecord` function validates or creates the user entity, providing the necessary foreign key for subsequent organization membership operations.

## Implementation Flow

### Resolving the Delegated Context

The entry point for delegated authentication resides in [`src/middleware/ensure-user/delegated.ts`](https://github.com/every-app/open-seo/blob/main/src/middleware/ensure-user/delegated.ts). The `resolveDelegatedContext` function orchestrates the entire flow by first validating the user record, then provisioning the dedicated organization:

```typescript
// src/middleware/ensure-user/delegated.ts
export async function resolveDelegatedContext(
  userId: string,
  userEmail: string,
): Promise<EnsuredUserContext> {
  const ensuredEmail = await ensureUserRecord(userId, userEmail);
  const organizationId = await ensureDelegatedOrganizationForUser(
    userId,
    ensuredEmail,
  );

  return {
    userId,
    userEmail: ensuredEmail,
    emailVerified: true,
    organizationId, // Format: `delegated-${userId}`
  };
}

```

### Generating the Organization Identity

The `ensureDelegatedOrganizationForUser` function in [`src/server/auth/delegated-organization.ts`](https://github.com/every-app/open-seo/blob/main/src/server/auth/delegated-organization.ts) constructs the organization metadata. It derives a human-readable name from the email prefix and generates a URL-safe slug combined with a hexadecimal representation of the user ID:

```typescript
// src/server/auth/delegated-organization.ts
export async function ensureDelegatedOrganizationForUser(
  userId: string,
  email: string,
) {
  const organizationId = `delegated-${userId}`;
  const name = `${email.split("@")[0] || userId} workspace`;
  const slug = `delegated-${slugify(email.split("@")[0] || userId)}-${toHex(
    userId,
  )}`;

  await AuthRepository.upsertDelegatedOrganization({
    id: organizationId,
    name,
    slug,
  });

  return organizationId;
}

```

### Persisting the Organization Record

The `AuthRepository` class in [`src/server/auth/repositories/AuthRepository.ts`](https://github.com/every-app/open-seo/blob/main/src/server/auth/repositories/AuthRepository.ts) provides the `upsertDelegatedOrganization` method. This performs an atomic insert-or-update operation against the `organization` table, ensuring idempotency across multiple requests:

```typescript
// src/server/auth/repositories/AuthRepository.ts
async function upsertDelegatedOrganization(input: {
  id: string;
  name: string;
  slug: string;
}) {
  await db
    .insert(organization)
    .values({
      id: input.id,
      name: input.name,
      slug: input.slug,
      logo: null,
      createdAt: new Date(),
      metadata: null,
    })
    .onConflictDoUpdate({
      target: organization.id,
      set: { name: input.name, slug: input.slug },
    });
}

```

## Multi-Organization Membership Support

Despite the automatic creation of a delegated organization, OpenSEO maintains full multi-organization capabilities through the **`member` table**. This many-to-many relationship table stores all organization memberships, allowing a single user to belong to numerous hosted organizations simultaneously.

When processing delegated requests, the system explicitly sets the `organizationId` in the `EnsuredUserContext` to the user-specific delegated organization. This isolation prevents data leakage between organizations while the underlying `findFirstOrganizationIdForUser` query (used in hosted contexts) continues to respect the user's broader membership portfolio. The architecture effectively partitions delegated sessions into their own workspaces without restricting the user's ability to access other organizations through different authentication flows.

## Summary

- OpenSEO creates a deterministic organization ID using the pattern `delegated-${userId}` for every delegated authentication session.
- The `resolveDelegatedContext` function in [`src/middleware/ensure-user/delegated.ts`](https://github.com/every-app/open-seo/blob/main/src/middleware/ensure-user/delegated.ts) orchestrates user validation and organization provisioning.
- `ensureDelegatedOrganizationForUser` generates human-readable names and URL-safe slugs based on user email and ID.
- `AuthRepository.upsertDelegatedOrganization` performs atomic upserts to maintain idempotency.
- The `member` table preserves multi-organization relationships, allowing users to maintain separate hosted organizations while using delegated auth.

## Frequently Asked Questions

### What is the format of the delegated organization ID?

OpenSEO uses the format `delegated-${userId}` to generate a unique, deterministic organization identifier for each user accessing the system via delegated authentication. This ensures the same user always receives the same organization ID across sessions while maintaining isolation from other users.

### Can a user belong to multiple organizations while using delegated auth?

Yes. The delegated organization is created in addition to any existing memberships in the `member` table. Users can belong to multiple hosted organizations while the delegated auth flow automatically provisions a dedicated workspace for their current session, with the `organizationId` explicitly set in the context to ensure proper data isolation.

### How does the system handle concurrent delegated auth requests?

The `upsertDelegatedOrganization` method uses database-level conflict resolution with `onConflictDoUpdate`, making the operation idempotent. Whether the organization already exists or needs creation, the repository method ensures consistent state without duplicate records or race conditions.

### Where is the delegated organization created in the codebase?

The creation flow spans three primary files: [`src/middleware/ensure-user/delegated.ts`](https://github.com/every-app/open-seo/blob/main/src/middleware/ensure-user/delegated.ts) initiates the context resolution, [`src/server/auth/delegated-organization.ts`](https://github.com/every-app/open-seo/blob/main/src/server/auth/delegated-organization.ts) generates the organization metadata, and [`src/server/auth/repositories/AuthRepository.ts`](https://github.com/every-app/open-seo/blob/main/src/server/auth/repositories/AuthRepository.ts) persists the record to the database.