Skip to main content

Organizations & Teams

Concordia uses Better Auth’s organization plugin to provide multi-tenant team management with role-based access control, member management, and invitation workflows.

Overview

Organizations are implemented in src/database/schemas/auth-schema.ts using Better Auth’s built-in organization features. This provides:
  • Multi-user organizations with hierarchical roles
  • Member invitation system
  • Active organization context per session
  • Organization-scoped resources (blogs, services, etc.)

Organization Schema

From src/database/schemas/auth-schema.ts (lines 94-105):

Organization Fields

text
required
Unique identifier for the organization
text
required
Display name of the organizationExample: "Acme Corporation"
text
required
Unique URL-friendly identifierExample: "acme-corp"
URL to organization logo image
timestamp
required
When the organization was created
text
JSON string for additional custom data
The slug field has a unique index ensuring no two organizations share the same URL path.

Member Management

From auth-schema.ts (lines 107-124):

Member Fields

text
required
Unique member relationship ID
text
required
Reference to the organizationOn Delete: CASCADE - members are deleted when organization is deleted
text
required
Reference to the userOn Delete: CASCADE - membership is removed when user is deleted
text
default:"member"
Member’s role within the organizationCommon roles: owner, admin, member
timestamp
required
When the user joined the organization

Member Indexes

Two indexes optimize member queries:
  1. By Organization: member_organizationId_idx - Fast lookup of all members in an org
  2. By User: member_userId_idx - Fast lookup of all orgs a user belongs to

Invitation System

From auth-schema.ts (lines 126-146):

Invitation Fields

text
required
Unique invitation identifier
text
required
Organization the invitation is for
text
required
Email address of the invitee
text
Role the invitee will have upon accepting
text
default:"pending"
Invitation status: pending, accepted, rejected, expired
timestamp
required
Expiration date/time for the invitation
timestamp
required
When the invitation was created
text
required
User who sent the invitation (must be org admin/owner)
Invitations automatically expire after the expiresAt timestamp. Check status before allowing acceptance.

Invitation Indexes

  1. By Organization: invitation_organizationId_idx - List all invitations for an org
  2. By Email: invitation_email_idx - Find pending invitations for a user

Session Context

From auth-schema.ts (lines 32-51), sessions track the active organization:
text
ID of the organization currently active in this sessionAllows users to switch between organizations they belong to
Users can belong to multiple organizations. The activeOrganizationId determines which org context is used for creating resources.

Relationships

From auth-schema.ts (lines 176-201), Drizzle relations define the connections:

Relationship Diagram

Organization Roles

Better Auth organization plugin supports hierarchical roles:

Default Roles

role
Full control over organization, including deletion
  • Manage all members and invitations
  • Delete organization
  • Change organization settings
  • Create and manage all resources
role
Administrative privileges
  • Invite and manage members
  • Manage organization settings
  • Create and manage resources
  • Cannot delete organization
role
Basic member access
  • View organization resources
  • Create resources under organization
  • Cannot invite users
  • Cannot change settings
Custom roles can be defined in Better Auth configuration to match your specific permission requirements.

Organization-Scoped Resources

Blog Posts

From blog_posts.schema.ts:
Blog posts can belong to an organization, enabling organization blogs.

Service Listings

From services_listings.schema.ts:
Services can be offered by organizations or individual providers.

Authors

From blog_authors.schema.ts:
The worksForId field links authors to organizations, establishing organizational attribution for content.

Invitation Workflow

  1. Create Invitation: Admin/owner creates invitation with email and role
  2. Send Email: System sends invitation link to email address
  3. User Accepts: User clicks link and accepts invitation
  4. Create Membership: System creates member record with specified role
  5. Update Invitation: Invitation status changes to accepted

Example Invitation Flow

Multi-Tenancy Pattern

Organizations enable multi-tenancy where:
  1. Users can belong to multiple organizations
  2. Resources can be scoped to an organization
  3. Sessions track active organization context
  4. Permissions are enforced per organization

Switching Organizations

Users switch active organization by updating their session:

Best Practices

Member Management

  1. Always verify user’s role before allowing privileged actions
  2. Use cascade deletes to maintain referential integrity
  3. Index lookups by both organizationId and userId for performance

Invitation Security

  1. Set reasonable expiration times (7-14 days)
  2. Verify invitation status before acceptance
  3. Check that email matches authenticated user
  4. Clean up expired invitations periodically

Organization Isolation

  1. Always filter queries by organization ID
  2. Use session’s activeOrganizationId for context
  3. Validate user has access to organization before showing resources
  4. Consider using RLS (Row Level Security) policies

Integration with Better Auth

The organization plugin is configured in Better Auth setup and provides:
  • Automatic organization context in auth session
  • Built-in member and invitation management APIs
  • Role-based access control helpers
  • Organization switching functionality
Refer to Better Auth Organization Plugin for implementation details.