6.2 KiB
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:
rolepermissions
Business Role
The UI and feature model mainly uses:
HRDEMPLOYEE
Business role is derived from session and membership state through getBusinessRole() in src/lib/auth/roles.ts.
Security Boundaries
1. Proxy route protection
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 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 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
organizationIdanduserId - HRD-only actions use
requireHRD() - review and audit routes validate higher privilege before returning data
Navigation Filtering
Sidebar and kbar filtering are driven by:
Supported visibility checks in nav items include:
requireOrgsystemRolerolepermissionbusinessRole
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
401or403from server-side helpers when access is invalid
This prevents bypass through copied links or bookmarked URLs.
API Protection
API protection follows these rules:
- Authentication is required through Auth.js session
- Organization membership is verified server-side
- Role-specific actions require additional server checks
- 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:
- Add navigation visibility only after server-side access rules are defined
- Use
requireOrganizationAccess()for organization-scoped routes - Use
requireHRD()for HRD-only operations - Use page guards for dashboard routes
- Protect file downloads through route handlers when files are sensitive
- Treat
use-nav.tsas 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.