Skip to content

Roles & Permissions

Complete reference for DoCurious user roles, admin sub-roles, vendor team roles, and community roles.

Role Architecture Overview

DoCurious uses a multi-context role model where each user has:

  1. One primary role (AppUser.role) — determines account type and base permissions
  2. Optional admin sub-role (AppUser.adminSubRole) — granular feature-area access for platform_admin/staff
  3. Optional vendor team membership (VendorTeamMember) — per-vendor role for shared vendor accounts
  4. Optional community membership (CommunityMember) — per-community role for moderation hierarchy

Users also hold contexts simultaneously (personal + school + business + platform). See the Architecture guide for the context model.


Primary Roles (9)

Every user has exactly one primary role stored in AppUser.role.

RoleDisplay NameDescriptionContext
userUserGeneral user — personal challenge doerPersonal only
studentStudentStudent within a schoolPersonal + School
parentParentParent/guardian linked to child accountsPersonal (parent is a relationship)
teacherTeacherTeacher within a schoolPersonal + School
school_adminSchool AdminSchool administratorPersonal + School
head_school_adminHead School AdminHead SA — can create other school adminsPersonal + School
vendorVendorMarketplace vendor who creates paid challengesPersonal + Business
staffStaffDoCurious internal staff (verification, support, etc.)Personal + Platform
platform_adminPlatform AdminFull platform administratorPersonal + Platform

Key Distinction

School admin ≠ Platform admin. School admins manage their school (roster, classes, assignments). Platform admins manage the DoCurious platform (users, content, config). They are completely separate contexts.

Role Hierarchy

platform_admin (full platform access)
  └── staff (feature-area access via adminSubRole)

head_school_admin (can create school_admin accounts)
  └── school_admin (manages school)
       └── teacher (manages classes, assigns challenges)
            └── student (completes challenges)
                 └── parent (views child activity, grants consent)

vendor (manages vendor business)
  └── vendor team members (owner > manager > contributor)

user (personal challenge doer — base role)

Student Account Tiers (COPPA)

Students have an additional accountTier field:

TierDescriptionRestrictions
tier_1_school_onlySchool acts as COPPA agentNo gifting, no saved list, limited social
tier_2_fullSchool + parent linkedFull platform features
standardUsers 13+Unrestricted access

Admin Sub-Roles (7)

When role is platform_admin or staff, the adminSubRole field controls which feature areas they can access.

Sub-RoleDisplay NameFeature Areas
superSuper AdminAll 10 feature areas
contentContent Adminchallenge_management, verification, content_moderation
schoolSchool Admin (Platform)school_management, user_management
vendorVendor Admin (Platform)vendor_management, user_management
supportSupportuser_management, content_moderation
analyticsAnalyticsanalytics
engineeringEngineeringsystem_config, analytics, audit_log, feature_flags

Feature Areas (10)

Feature AreaDescription
user_managementView/edit/suspend user accounts
challenge_managementApprove/reject/edit challenges
verificationReview and verify track records
content_moderationHandle flagged content and reports
school_managementManage school accounts and DPAs
vendor_managementManage vendor accounts and payouts
analyticsView platform analytics dashboards
system_configManage system configuration
audit_logView audit trail
feature_flagsToggle feature flags

Permission Matrix

Sub-RoleUsersChallengesVerifyModerateSchoolsVendorsAnalyticsConfigAuditFlags
superxxxxxxxxxx
contentxxx
schoolxx
vendorxx
supportxx
analyticsx
engineeringxxxx

Vendor Team Roles (3)

Vendors can have multiple team members with different access levels, stored in the VendorTeamMember table.

RoleDescriptionPermissions
ownerBusiness ownerFull access: challenge CRUD, orders, analytics, team management, payouts
managerOperations managerChallenge CRUD, order management, analytics (no payout access)
contributorContent contributorCreate/edit challenges only (no orders, analytics, or team management)

Database Model

prisma
model VendorTeamMember {
  id        String   @id @default(uuid())
  userId    String
  vendorId  String
  role      String   @default("contributor")  // owner, manager, contributor
  invitedBy String?
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  @@unique([userId, vendorId])
}

Community Roles (4)

Each community has its own role hierarchy, stored in the CommunityMember table.

RoleDescriptionPermissions
ownerCommunity creatorFull control: settings, roles, delete community
adminCommunity administratorManage members, edit settings, moderate content
moderatorCommunity moderatorModerate posts, mute/warn members, manage flags
memberRegular memberPost, comment, react, participate

Membership Statuses

StatusDescription
activeFull member with role-based access
pendingJoin request awaiting approval
invitedInvitation sent but not yet accepted
bannedRemoved from community

Database Model

prisma
model CommunityMember {
  id          String   @id @default(uuid())
  userId      String
  communityId String
  role        String   @default("member")  // owner, admin, moderator, member
  status      String   @default("active")  // active, pending, invited, banned
  joinedAt    DateTime @default(now())
  updatedAt   DateTime @updatedAt
  @@unique([userId, communityId])
}

Multi-Context Model

Users can hold multiple contexts simultaneously. The primary role maps to contexts via buildContextsFromLegacyRole() in contextHelpers.ts.

ContextWhen ActiveSub-Roles
personalAlways (every user)
schoolWhen user has a school rolestudent, teacher, school_admin, head_school_admin
businessWhen user is a vendorowner, manager, contributor
platformWhen user is staff or platform_adminstaff, admin

Context Switching

The UI's context switcher (header dropdown) allows users to switch between their active contexts. Each context shows different navigation, dashboards, and feature sets.


Route Guards

The frontend enforces role-based access through layered route guards:

GuardPurposeExample
AuthGuardRequires authenticationAll protected routes
ContextGuardRequires specific context + optional roleContextGuard({ context: 'school', roles: ['teacher', 'school_admin'] })
AdminRoleGuardRequires admin sub-role with specific feature areaAdminRoleGuard({ features: ['user_management'] })
TierGuardRequires minimum account tierTierGuard({ minTier: 'tier_2_full' })
AgeGuardRequires minimum ageAgeGuard({ minAge: 13 })
ConsentGuardRequires parental consentCOPPA-protected features
ParentGuardRequires parent relationshipParent dashboard routes

Backend Implementation

Prisma Schema

The role system lives in three tables:

  • app_usersrole (String) + adminSubRole (String, nullable)
  • vendor_team_membersuserId + vendorId + role
  • community_membersuserId + communityId + role + status

Middleware

MiddlewareFilePurpose
requireRole(...roles)server/src/middleware/requireRole.tsCheck primary role
requireAdminFeature(...features)server/src/middleware/requireRole.tsCheck admin sub-role has feature area access
requireAuthserver/src/middleware/auth.tsVerify JWT, attach req.user

JWT Token Payload

typescript
interface TokenPayload {
  userId: string
  role: string           // Primary role
  adminSubRole?: string  // Admin sub-role (when applicable)
}

The adminSubRole is included in both access and refresh tokens so that requireAdminFeature() middleware can check permissions without a database query.


FE Type Definitions

TypeFileValues
UserRolesrc/types/user.types.ts9 primary roles
AdminRolesrc/types/admin.types.ts7 admin sub-roles
ADMIN_PERMISSIONSsrc/types/admin.types.tsSub-role → feature area mapping
SchoolRolesrc/types/context.types.tsstudent, teacher, school_admin, head_school_admin
BusinessRolesrc/types/context.types.tsowner, manager, contributor
PlatformRolesrc/types/context.types.tsstaff, admin
CommunityRolesrc/types/community.types.tsowner, admin, moderator, member
MembershipStatussrc/types/community.types.tsactive, pending, invited, banned

DoCurious Platform Documentation