Fair Supply LogoFair Supply - Docs

Accounts Domain

The Accounts domain handles multi-tenancy, user management, and authentication through Auth0 integration.

Entity Hierarchy

Key Concepts

EntityDescription
AccountTop-level tenant container (like a company). All data is scoped to an account.
UserIndividual person. Can belong to multiple accounts with different roles.
SessionTracks which account a user currently has selected (stored in Redis).
Auth0 OrganisationExternal SSO tenant in Auth0, linked 1:1 with Account.

Multi-Tenancy Model

Users can belong to multiple accounts, each with a specific role:

Roles are defined by the AccountRole enum in user.graphql:

RolePermissions
ADMINFull access, can manage members
MEMBERStandard access, can view and edit

Account Selection Flow

  • Session stores selectedAccountId - all queries scope to this account
  • Switching accounts updates the session, not the user record
  • Sessions are stored in Redis with TTL-based expiration

Authorization

Use case authorization is handled by requireAccountAdmin() in account-authorization.ts:

import { requireAccountAdmin } from './account-authorization';

// Verifies the user is an admin of the account, its parent account, or is FSAdmin
await requireAccountAdmin(accountId, userId, isFSAdmin);

Business Rules

  1. Users must belong to at least one account
  2. Users with multiple accounts must select one before proceeding
  3. Admins can manage members; members can view only
  4. New users must complete registration before accessing features
  5. Account names should stay in sync with Auth0 org names
  6. Sessions expire after inactivity (TTL-based)

Common Operations

Getting the Current User and Selected Account

import { requireAccount } from '@/lib/auth';

const { userId, accountId } = await requireAccount();
// Redirects if unauthenticated; throws UnauthorizedError if no account selected.

Creating an Account from an Auth0 Organisation

Accounts are provisioned from a selected Auth0 organisation, not created directly from name + email.

import { CreateAccountFromAuth0Org } from '@repo/core';

const useCase = new CreateAccountFromAuth0Org();
const account = await useCase.execute({
  auth0OrgId: 'org_abc123',
  auth0OrgName: 'Acme Corp',
  userId,
});

Inviting a User to an Account

import { InviteUserToAccount } from '@repo/core';

const useCase = new InviteUserToAccount();
await useCase.execute({
  accountId: 'account-123',
  email: 'user@example.com',
  inviterName: 'Alice Admin',
  userId,
  isFSAdmin,
});

New members join via the Auth0 invitation flow with the MEMBER role by default. Use UpdateUserRole to promote to ADMIN.

Switching the Selected Account

import { SetSelectedAccount } from '@repo/core';

const useCase = new SetSelectedAccount();
await useCase.execute({
  userId,
  accountId: 'account-456',
});
// Session now scoped to new account

Auth0 Integration

Account creation and user management sync with Auth0:

Platform ActionAuth0 Action
Create accountCreate Auth0 Organisation
Add userInvite to Auth0 Organisation
Remove userRemove from Auth0 Organisation
Update account nameUpdate Auth0 Organisation name

Important Considerations

  • Auth0 API calls cannot be rolled back in transactions
  • If Neo4j write succeeds but Auth0 fails, handle orphaned records
  • Use background jobs for Auth0 sync when possible
TypeLocation
GraphQL Schemapackages/core/src/infrastructure/neo4j/schemas/account.graphql
Repositorypackages/core/src/infrastructure/repositories/account-repository.ts
Use Casespackages/core/src/application/use-cases/account/
Auth0 Integrationpackages/core/src/infrastructure/auth0/
Session Managementpackages/core/src/infrastructure/redis/session.ts

On this page