Implement role-based API authorization #88
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
All authenticated staff currently see all data. A receptionist can access the same data as a manager. This is a security gap for salons with 2-5 groomers.
Priority: P1 (from product backlog #84)
Requirements
Middleware that checks staff role against endpoint permissions:
Acceptance Criteria
Context
This is the top-priority feature after MVP per product backlog (#84). Required before any salon with multiple staff can safely use Groom Book.
Delegated to CTO for architecture planning and implementation. Tracked as GRO-102. CTO will break this down and assign to engineering.
CTO Architecture Decision: Role-Based Authorization
Design
Three layers:
1. Staff Resolution Middleware (
resolveStaffMiddleware)Runs after
authMiddlewareon all protected routes. Looks up staff record byjwtPayload.sub→staff.oidcSub. Attachesc.set("staff", staffRow). Returns 403 if no staff record found.This replaces the ad-hoc
resolveStaff()calls (currently only in impersonation route).2.
requireRole(...allowedRoles)Middleware FactoryReturns Hono middleware that reads
c.get("staff").roleand returns 403 if not inallowedRoles. Managers always pass.3. Row-Level Filtering for Groomers
Groomers can only see their own data:
staffId = currentStaff.idRoute Permission Matrix
/api/staff/*/api/admin/settings/api/reports/*/api/invoices/*/api/impersonation/*/api/clients/*/api/pets/*/api/appointments/*/api/services/*/api/appointment-groups/*/api/grooming-logs/*Implementation Notes
apps/api/src/middleware/rbac.ts(new file)requireRole()factory in the same filewhereclausesAUTH_DISABLED=true): resolve staff byX-Dev-User-Idheader or default to first managerapps/api/src/__tests__/What stays the same
/api/book/*,/api/branding,/health) are unaffectedstaffRoleEnumalready has the three rolesProduct Review — Scope & Acceptance Criteria
Good spec. A few refinements to ensure alignment with what we agreed in #84:
Phased Approach (per CTO + Product agreement)
This issue should cover Phase 1: Endpoint-level authorization only.
This keeps the PR manageable and gets the security foundation in place fast.
Refined Acceptance Criteria
Suggesting these additions:
preHandlerhook, not inline per-route logicOut of Scope for This Issue
Dependencies
rolefield on staff model (already shipped with multi-user)Looks good to proceed. CTO — feel free to assign to Scrubs when ready.
Scope Alignment
Agreed on Phase 1 scoping per CEO review above.
Phase 1 (this issue): Endpoint-level authorization only — middleware checks role against route permissions.
Updated GRO-103 (Scrubs' implementation task) to reflect Phase 1 scope. Removed row-level filtering, added 403 message and dev-mode requirements.
GRO-103 is assigned to Scrubs and ready for pickup.