Files
nextjs-elysia-allaos/docs/checklist-phase2-middleware.md
phaichayon 043edff93a setup
2026-04-26 00:15:22 +07:00

7.6 KiB

Phase 2: Branch Middleware (ElysiaJS) - Checklist

Overview

Implement branch validation middleware to enforce multi-tenant access control, integrate with Keycloak groups, and provide branch context to all routes.

📋 Completed Tasks

Middleware Implementation

  • Create src/middleware/branch.ts
    • Define BranchContext interface
    • Define AccessibleBranch interface
    • Implement branchMiddleware using Elysia's derive
    • Add branch validation logic
    • Add error handling for unauthorized access
    • Add error handling for inactive branches
    • Implement default branch selection
    • Export helper functions (canAccessBranch, getDefaultBranch)

Type Safety

  • Fix TypeScript type errors
    • Correct database import path
    • Make BranchContext extend Record<string, unknown>
    • Handle nullable isActive field

Documentation

  • Add comprehensive JSDoc comments
  • Add usage examples
  • Document TODOs for authentication integration

🎯 Key Features Implemented

1. Branch Context Injection

  • Automatically injects branch context into all routes
  • Provides currentBranchId, currentBranchCode, userId
  • Exposes accessibleBranches for UI controls
  • Exposes userGroups for permission checks

2. Branch Access Validation

  • Validates x-branch-id header
  • Checks user's Keycloak groups
  • Prevents cross-branch access
  • Blocks inactive branches

3. Error Handling

  • Clear error messages for unauthorized access
  • Helpful error for missing branch access
  • Inactive branch blocking

4. Helper Functions

  • canAccessBranch() - Check if user can access specific branch
  • getDefaultBranch() - Get user's default branch
  • getUserAccessibleBranches() - Fetch accessible branches from DB

📊 Middleware Flow

Request
  ↓
Extract User ID & Groups (JWT/Session)
  ↓
Get Accessible Branches from DB
  ↓
Check x-branch-id Header
  ↓
Validate Branch Access
  ↓
Inject Branch Context
  ↓
Route Handler

🔧 Usage Examples

Basic Usage

import { Elysia } from "elysia";
import { branchMiddleware } from "@/middleware/branch";

const app = new Elysia()
  .use(branchMiddleware)
  .get("/customers", async ({ currentBranchId, userId }) => {
    // currentBranchId is automatically available
    const customers = await getCustomersByBranch(currentBranchId);
    return customers;
  });

Branch-Specific Operations

app.get("/quotations/:id", async ({ params, currentBranchId }) => {
  const quotation = await getQuotationById(params.id, currentBranchId);

  if (!quotation) {
    throw new Error("Quotation not found or access denied");
  }

  return quotation;
});

Multi-Branch UI Support

app.get("/api/me/branches", async ({ accessibleBranches }) => {
  // Return all branches user can access for UI dropdown
  return accessibleBranches;
});

Manual Access Check

import { canAccessBranch } from "@/middleware/branch";

app.post(
  "/admin/transfer",
  async ({ body, currentBranchId, accessibleBranches }) => {
    const targetBranchId = body.targetBranchId;

    if (!canAccessBranch(accessibleBranches, targetBranchId)) {
      throw new Error("Cannot transfer to branch you don't have access to");
    }

    // Proceed with transfer
  },
);

🚨 Important Notes

Authentication Integration (TODO)

The middleware currently has TODOs for proper authentication:

// TODO: Implement proper JWT/session extraction
function extractUserIdFromRequest(request: Request): string | null {
  // Replace with actual JWT verification
  const token = request.headers.get("authorization")?.replace("Bearer ", "");
  const decoded = jwt.verify(token, process.env.JWT_SECRET);
  return decoded.userId;
}

This will be implemented in Phase 3: Keycloak Integration.

Header Requirement

All requests must include the x-branch-id header to specify the target branch:

curl -H "x-branch-id: <branch-uuid>" \
     -H "authorization: Bearer <jwt-token>" \
     http://localhost:3000/api/customers

If no header is provided, the middleware uses the user's first accessible branch as default.

Branch Inactivation

When a branch is marked as inactive (isActive: false):

  • Users cannot switch to that branch
  • Existing sessions on that branch will fail
  • Data remains intact but inaccessible

🔍 Testing Checklist

Unit Tests (TODO)

  • Test user extraction from JWT
  • Test group extraction from JWT
  • Test accessible branches fetching
  • Test branch validation (valid branch)
  • Test branch validation (invalid branch)
  • Test branch validation (inactive branch)
  • Test default branch selection
  • Test error messages

Integration Tests (TODO)

  • Test middleware with real Elysia app
  • Test cross-branch access prevention
  • Test multiple branch access
  • Test header-based branch switching
  • Test unauthorized user handling

📝 Known Limitations

  1. Authentication Not Yet Implemented

    • Current implementation returns null for user ID
    • Must be completed in Phase 3
    • Testing requires mock authentication
  2. Branch Group Mapping Hardcoded

    • Currently maps ["alla", "onvalla"]
    • Could be made configurable
    • Consider dynamic branch group discovery
  3. Performance

    • Fetches branches on every request
    • Consider caching accessible branches
    • Could store in session/JWT

🔄 Next Steps

Immediate (Phase 2)

  • Write unit tests
  • Write integration tests
  • Add logging/middleware
  • Document API changes

Phase 3: Keycloak Integration

  • Implement JWT verification
  • Implement user ID extraction
  • Implement group extraction
  • Add token refresh logic
  • Test with real Keycloak

Phase 5: Controllers Update

  • Remove /:branch path parameters
  • Update all routes to use middleware context
  • Add branch context to responses
  • Update API documentation

📊 File Changes

Created Files

  • src/middleware/branch.ts (205 lines)

Modified Files

  • None yet (controllers will be updated in Phase 5)

Documentation Files

  • docs/checklist-phase2-middleware.md

Success Criteria

  • Middleware validates branch access
  • Prevents cross-branch data access
  • Provides branch context to routes
  • Handles inactive branches
  • Returns clear error messages
  • TypeScript types are correct
  • Helper functions work correctly
  • Documentation is complete
  • Unit tests pass
  • Integration tests pass
  • JWT authentication works (Phase 3)

🎯 Security Considerations

  1. Branch Isolation

    • Users can only access their assigned branches
    • Cross-branch requests are blocked
  2. Header Validation

    • Validates branch exists and is active
    • Checks user has permission
  3. Session Security (Pending Phase 3)

    • JWT tokens must be verified
    • Token expiration must be checked
    • Revoked tokens must be rejected
  4. Error Messages

    • Don't expose internal structure
    • Don't reveal other branches
    • Generic "access denied" for security

📚 References


Phase 2 Status: CORE COMPLETED (Tests Pending) Completion Date: 2026-04-23 Next Phase: Phase 3 - Keycloak Integration Blocking: Tests, Phase 3 (Authentication)