MessMasterKnowledge hub
Features Guide

Member Management and Role-Based Access in MessMaster

In a spreadsheet, everyone with the link can edit everything. In MessMaster, access is controlled by role — and every action is authenticated and authorized before it reaches the database.

A role and permission matrix for members, managers, and owners
A role and permission matrix for members, managers, and owners
Contents6 sections
  1. The role hierarchy
  2. What each role can do
  3. How permissions are enforced
  4. Member lifecycle
  5. Role transfer
  6. Security model
  • Member: can view mess data, enter meals, submit payments, and update their own profile.
  • Meal manager: can do everything a member can, plus manage members, rates, costs, payments, and month-end closure.

Your available controls and calculations depend on your role, mess status, deployment, and current server data. This guide uses fictional examples and does not replace the values shown in the app.

Guide status: Labels, permissions, calculation rules, rates, costs, exports, and available controls can vary by deployment and mess configuration. Use the current screen as the source to check and verify saved records after important changes.

Before you start

  • You know whether you are a member or meal manager in the current mess.
  • You understand that disabled buttons are a UI hint, not the only permission boundary.
  • You know that the server validates every action independently.

The role hierarchy

MessMaster has three primary roles with escalating permissions:

  1. Owner — platform administrator. Manages the entire MessMaster platform, not a single mess. Logs in with bcrypt against Owner.passwordHash, not Supabase.
  2. Meal Manager — mess administrator. Creates the mess, manages members, sets rates, reviews payments, and executes month-end closure.
  3. Member — daily participant. Enters meals, views balances, submits payments, and reports mistakes.

Composite identity

A user is a member of a mess via MessMember with a composite key: mess_code + membership_id. A user can belong to multiple messes. Always scope mess data by messCode — never assume one user equals one mess.

What each role can do

Member

  • View mess dashboard, balances, and meal history
  • Enter and edit own meal entries
  • View bazar and cost summaries
  • Submit payments with transaction references
  • Upload receipts (in the correct private mess context)
  • Receive notifications
  • Update own profile

Meal Manager

Everything a member can do, plus:

  • Manage member roster (approve, reject, remove)
  • Set and update meal rates, bazar duty, and shared costs
  • Review and approve/reject payments
  • Execute month-end closure and transition
  • View and export statements
  • Transfer role to another member

Owner

  • Platform-level administration
  • Approve or reject mess creation requests
  • Manage platform-level settings
  • View all messes (platform-level access)
  • Not tied to a single mess

How permissions are enforced

Permissions are enforced at three layers — not just by hiding buttons:

Layer 1: JWT verification

requireAuth verifies the JWT locally with jwt.verify(token, SUPABASE_JWT_SECRET). It does not call Supabase. If the token is invalid or blacklisted, the request is rejected before reaching any business logic.

Layer 2: Role guard

Role guards check whether the user has the required role for the action:

  • requireMessManager — mess admin access
  • requireMessMember — member access
  • requireOwnerRole — platform owner
  • requireOwnerOrManagerRole — shared admin

Layer 3: Status guard

messStatusGuard() only admits requests when the mess status === "active". A soft-deleted, pending, or rejected mess cannot be operated on, even with a valid token and role.

Why three layers?

A disabled button in the UI is a convenience hint. The server independently validates every action. Even if someone bypasses the UI, the server rejects unauthorized requests. This defense-in-depth approach prevents privilege escalation.

Member lifecycle

Joining a mess

  1. A member creates an account via Supabase Auth.
  2. The member enters the mess code to request joining.
  3. The meal manager reviews the request.
  4. The manager approves or rejects the member.
  5. If approved, the member's status changes to active.
  6. The member can now enter meals and view mess data.

Member statuses

  • Pending approval — waiting for manager review
  • Active — full access to mess features
  • Rejected payment — payment was rejected; limited access for recovery
  • Removed — manager removed the member; access revoked

Status gate

requireAuth enforces a portal-member status gate. Unconfirmed members are rejected, with narrow exemptions for specific recovery flows (e.g., reading a rejection reason, restoring a soft-deleted mess).

Role transfer

A meal manager can transfer their role to another active member. This is useful when the current manager leaves the mess.

  1. The manager initiates a role transfer.
  2. The target member receives an offer.
  3. The member accepts or declines.
  4. If accepted, the roles swap — the old manager becomes a member, and the member becomes the manager.
  5. The transfer is logged and irreversible.

Important

Role transfer does not change ownership of the mess data. It only changes who has manager permissions. The mess itself remains unchanged.

Security model

Token invalidation

Token invalidation is layered on top of stateless JWTs:

  • Logout → exact-token blacklist (60s TTL cache, positive hits only)
  • Session revoke → revoked-before:<uid>:<ts> marker compared to token iat
  • Owner sessions → OwnerSession rows add a second expiry check

Why no negative caching?

The blacklist caches positive hits only. Negative caching was deliberately avoided — it would let a just-logged-out token stay valid for the cache duration.

Rate limiting

Per-purpose rate limiters prevent abuse:

  • loginLimiter — login attempts
  • registerLimiter — registration attempts
  • passwordResetLimiter — password reset requests
  • apiLimiter — general API calls
  • emailTriggerLimiter — email sending

trust proxy is set to 1 (not true) in production so IP-keyed limiters see the real client behind Vercel/Render — a single hop.

Important: Role-based access is enforced by the server, not just the UI. Disabled buttons are a convenience, not a security boundary. Every action is validated against the JWT, role guard, and mess status guard before execution. Your available actions depend on your role, mess status, and current server configuration.

Tags

  • #roles
  • #access
  • #security
  • #features