Developer Guide
A “Domain” represents a distinct organization, campus, or physical environment. Every piece of spatial data and every user account is scoped to one or more of these domains.
The Domain model is managed by the Auth Service. It dictates not just the existence of an organization, but the rules for how users can join and authenticate against it.
export interface IDomain extends Document {
name: string;
subdomains: Types.ObjectId[];
authStrategy: "internal" | "oidc";
totpSecrets: IDomainTOTPSecrets;
ssoConfig?: ISSOConfig;
isVisibleFromOutside?: boolean;
}Key Field Breakdown:
Because CrowdVision separates Authentication and Twin data into different databases, multi-tenancy must be enforced symmetrically across the service boundary.
erDiagram
ACCOUNT {
String _id
IDomainMembership[] memberships
}
DOMAIN {
String _id
String name
String authStrategy
}
BUILDING {
String id
String[] domains
}
ACCOUNT ||--o{ DOMAIN : "joins via membership"
BUILDING }|--|{ DOMAIN : "is assigned to"Account contains an array of memberships. Each membership object points to a specific domainName and assigns a role.Building does not belong to a user. Instead, it contains an array of domains (strings).How Isolation is Enforced: When a user attempts to load the buildings for university.edu, the frontend calls GET /twin/buildings/university.edu.
The twin-service intercepts the request, verifies the JWT signature, and checks the embedded payload. If the payload does not contain a membership for university.edu, the request is rejected with 403 Forbidden. If authorized, it queries its own database for Building.find({ domains: "university.edu" }).
This guarantees absolute data isolation between tenants without requiring cross-service HTTP calls.
Organizations have wildly different security requirements. The authStrategy field dictates how CrowdVision handles sign-ins for a specific domain.
Strategy 1: internal This is the default strategy. User credentials (passwords) are hashed and managed entirely within CrowdVision’s Auth database.
totpSecrets subdocument. These are permanent, secure cryptographic seeds generated upon domain creation. They are used to generate the temporary 6-digit invite codes (or QR codes) that allow users to claim higher privileges (like business_admin or business_staff).Strategy 2: oidc (Enterprise SSO)
For enterprise clients using platforms like Keycloak, Auth0, or Entra ID, CrowdVision acts as an OIDC relying party.
When a domain uses oidc, it must populate the ssoConfig subdocument:
export interface ISSOConfig {
issuerUrl: string;
clientId: string;
clientSecret: string;
}The PKCE Security Flow:
To prevent state-tampering and CSRF attacks during the SSO redirect dance, CrowdVision implements strict PKCE (code_challenge_method: S256).
Security Mandate: Never encode sensitive data (like the target domain or username) directly into the state parameter payload (e.g., as base64 JSON). A malicious actor can easily manipulate client-side state payloads. Always rely on server-side lookups using an opaque state key.
Once the OIDC exchange completes successfully, the Auth Service maps the external user to an internal Account, embeds the IDomainMembership (using the externalId), and issues a standard CrowdVision JWT. From the perspective of the Twin or Notification services, an OIDC user is completely indistinguishable from an internal user.