Files
alla-tms/docs/nav-rbac.md
2026-07-16 09:53:14 +07:00

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:

  • 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.

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 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:

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.

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.