logicspike/docs

Authentication & Team

Domain Model: Team Management

1. Core Entities

Membership

Represents the link between a User and a Tenant.

  • Attributes:
    • id: UUID
    • userId: UUID
    • tenantId: UUID
    • roleId: UUID
    • isOwner: Boolean (Immutable flag for the tenant creator, bypasses role checks)
    • joinedAt: Timestamp
    • status: ACTIVE | SUSPENDED

Invitation

A request for a user (existing or new) to join a Tenant.

  • Attributes:
    • id: NanoID (Publicly shareable token)
    • tenantId: UUID
    • email: String (The intended recipient)
    • roleId: UUID (The role they will get)
    • invitedBy: UUID (UserId of sender)
    • expiresAt: Timestamp (default 7 days)
    • redeemedAt: Timestamp | Null
    • redeemedBy: UUID | Null
    • status: PENDING | ACCEPTED | EXPIRED | REVOKED

Role

A collection of permissions.

  • Types:
    1. System Roles: Hardcoded, available to all tenants (e.g., Viewer, Editor, Admin). Cannot be modified.
    2. Custom Roles: Created by Tenant Admins. Scoped to tenantId.
  • Attributes:
    • id: UUID
    • tenantId: UUID | Null (Null = System Role)
    • name: String
    • permissions: Set (JSON)

Permission

A granular capability string.

  • Format: resource:action
  • Examples:
    • blog:posts.create
    • blog:posts.publish (Separate from create!)
    • team:members.invite
    • team:members.remove
    • billing:invoices.read

2. Relationships


3. Business Logic & Invariants

Invitation Lifecycle (State Machine)

  1. Draft: Created.
  2. Sent: Email dispatched. Status -> PENDING.
  3. Click: User validates token. Checks expiresAt.
  4. Accept:
    • IF Email matches logged-in user OR Invitation allows "Any Email" (configurable? No, safest is Strict Match).
    • THEN: Create Membership, Status -> ACCEPTED.
  5. Expire: CurrentTime > expiresAt. Status -> EXPIRED.
  6. Revoke: Admin cancels invite. Status -> REVOKED. Token invalid.

Role Logic

  • Immutable Owners: The Owner role cannot be deleted or edited. There must always be at least one Owner per tenant (usually).
  • Self-Destruct Prevention: An Admin cannot remove their own team:manage permission (or allow them to, but warn).
  • Hierarchy: A user with team:manage cannot assign a role "higher" than their own (needs a simplified "Role Level" integer to enforce, e.g., Owner=100, Admin=50, Member=10).

Security Constraints

  • Cross-Tenant Isolation: A Custom Role from Tenant A cannot be assigned to a user in Tenant B.
  • Orphan Prevention: A Tenant must validly exist to invite members.
Authentication & Team