# Navigation and RBAC ## Overview This repository uses an Auth.js-based access model with server-side authorization as the real security boundary. Navigation filtering exists for usability only. It helps show the right menus to the right users, but it is not the thing that protects data. Real protection is enforced in: - `src/proxy.ts` - page guards under `src/lib/auth/page-guards.ts` - session helpers under `src/lib/auth/session.ts` - route handlers under `src/app/api/**` ## Core Concepts ### User Authenticated person from Auth.js session. ### Organization The active tenant/workspace from `session.user.activeOrganizationId`. ### Membership The user's relation to the active organization. This is loaded server-side and includes: - `role` - `permissions` ### Business Role The UI and feature model mainly uses: - `HRD` - `EMPLOYEE` Business role is derived from session and membership state through `getBusinessRole()` in [src/lib/auth/roles.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/lib/auth/roles.ts). ## Security Boundaries ### 1. Proxy route protection [src/proxy.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/proxy.ts) blocks unauthenticated access to: - `/dashboard/**` - protected API namespaces such as: - `/api/training-records/**` - `/api/notifications/**` - `/api/announcements/**` - `/api/audit-logs/**` - other protected modules This ensures unauthenticated users cannot directly browse protected pages or call protected APIs. ### 2. Server-side session helpers [src/lib/auth/session.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/lib/auth/session.ts) is the main authorization entry point. Important helpers: - `requireSession()` - `requireOrganizationAccess()` - `requireHRD()` - `requireEmployee()` These helpers: - validate authentication - validate active organization context - verify membership - derive role-based access - return organization-scoped access data for downstream queries ### 3. Page-level guards [src/lib/auth/page-guards.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/lib/auth/page-guards.ts) handles protected dashboard navigation and redirect behavior. Important guards: - `requireEmployeeDashboardAccess()` - `requireHRDDashboardAccess()` These are used by dashboard pages so direct URL access is also protected, not only sidebar visibility. ### 4. Route handler authorization Every sensitive route handler must still verify organization and role server-side. Examples: - employee-owned training data is scoped by `organizationId` and `userId` - HRD-only actions use `requireHRD()` - review and audit routes validate higher privilege before returning data ## Navigation Filtering Sidebar and kbar filtering are driven by: - [src/config/nav-config.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/config/nav-config.ts) - [src/hooks/use-nav.ts](/d:/WY-2569/HRD/training-system-minimal-refactor/src/hooks/use-nav.ts) Supported visibility checks in nav items include: - `requireOrg` - `systemRole` - `role` - `permission` - `businessRole` This filtering is based on the current Auth.js session in the client. Important: hiding a menu item does not grant or remove backend access. It only controls what the user sees in the UI. ## Role-Based Menu Behavior Current practical behavior: ### Employee Employee users can see employee-facing areas such as: - Dashboard - Training Records - Announcements - Notifications Employee access is still server-scoped to their own allowed data. ### HRD HRD users can access broader organization operations such as: - Pending Review - Employees - Courses - Training Policy - Import Employees - Reports - Audit Logs - Master Review These pages also require server-side HRD validation, not only visible nav items. ### Super Admin Super admin users can access system-level organizer management and can be elevated into organization context when required by server-side helpers. ## Direct URL Protection The system explicitly protects direct navigation to pages even if a user manually enters a URL. Examples: - unauthenticated users are redirected at the proxy layer - authenticated but unauthorized users are redirected by page guards - API routes return `401` or `403` from server-side helpers when access is invalid This prevents bypass through copied links or bookmarked URLs. ## API Protection API protection follows these rules: 1. Authentication is required through Auth.js session 2. Organization membership is verified server-side 3. Role-specific actions require additional server checks 4. Data queries are scoped by organization and, when needed, by the current user This is the main security boundary for all business data. ## File Download Protection Protected files must not be served only by direct public URLs. Current protected examples: - training certificates: - `/api/training-records/[id]/certificates/[certificateId]/download` - announcement attachments: - `/api/announcements/[id]/attachment` These endpoints verify: - authentication - organization scope - record-level access - HRD/employee visibility rules This prevents unauthorized users from opening files just because they know or guess the storage path. ## Recommended Maintenance Rules When adding or changing protected features: 1. Add navigation visibility only after server-side access rules are defined 2. Use `requireOrganizationAccess()` for organization-scoped routes 3. Use `requireHRD()` for HRD-only operations 4. Use page guards for dashboard routes 5. Protect file downloads through route handlers when files are sensitive 6. Treat `use-nav.ts` as UX logic, not as a security mechanism ## What To Avoid Do not reintroduce: - Clerk-specific authorization assumptions - client-side-only protection for sensitive features - public file URLs for protected business documents - page access that depends only on hidden sidebar items ## Summary The current model is: - Auth.js for authentication - proxy-based route gating for unauthenticated requests - server-side authorization helpers for real access control - client-side nav filtering for usability That combination keeps the UI predictable while ensuring that direct URLs, APIs, and file downloads are protected at the server boundary.