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_users — role (String) + adminSubRole (String, nullable)
  • vendor_team_members — userId + vendorId + role
  • community_members — userId + 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