MFG Lite - Phase 0 Development
This document provides a comprehensive technical breakdown of all features, database tables, Prisma models, security enhancements, server actions, role-based access control (RBAC) rules, and UI components implemented in Phase 0 of the MFG Lite ERP platform.
Executive Summary & Database Scope
Phase 0 establishes the multi-tenant architecture, authentication, role-based authorization (RBAC), security defenses, and enterprise master data isolation across two isolated databases:
MfgLiteCrystal(Auth Database —prisma/schema.auth.prisma): Manages tenant user credentials, hashed OTP generation, single-session enforcement, role assignments, and tenant subscriptions.- Database Tables & Models:
client(Client),linkclientmodule(LinkClientModule),linkusermodulerole(LinkUserModuleRole),clientsubscription(ClientSubscription),users(User).
- Database Tables & Models:
MfgLite(Main ERP Database —prisma/schema.prisma): Manages enterprise organization profiles, manufacturing units/branches, and GST master data.- Database Tables & Models:
Company(Company),CompanyBranch(CompanyBranch),GstState(GstState).
- Database Tables & Models:
🚀 Additional Value-Add Features & Infrastructure Enhancements
In addition to the core Phase 0 requirement items, the following 5 critical infrastructure, performance, and security enhancements were implemented:
- Brute-Force Lockout & IP Rate Limiting:
- 5-failed-attempt lockout threshold on OTP verification (
otpattempts >= 5). - In-memory sliding-window IP rate limiter restricting
/api/auth/send-otpto a maximum of 3 requests per 60 seconds per IP address.
- 5-failed-attempt lockout threshold on OTP verification (
- Single-Session Takeover Modal Dialog:
- Real-time session UUID checking. When a second device logs in, the original device detects the takeover during polling and displays a popup dialog blocking UI access until the user re-authenticates.
- Dual Prisma Client Build Automation (
postinstall):- Automated dual-schema client generation script in
package.jsonensuring seamless compilation for bothMfgLiteandMfgLiteCrystaldatabases.
- Automated dual-schema client generation script in
- Full Dark Mode Styling Support:
- Complete Tailwind
dark:styling across the Login page, textboxes, modals, layout headers, and settings, with hydration safety inline scripts.
- Complete Tailwind
- Zero-Downtime Session Cookie Expiration Handling:
- Clean cookie deletion helper (
clearSession()) using explicitMax-Age=0andPath=/HTTP headers upon token expiry or 30-minute idle inactivity, eliminating stuck loading screens.
- Clean cookie deletion helper (
Architecture Diagram
graph TD
Client["Next.js App Router (Client / UI)"] --> Edge["Edge Middleware (src/middleware.ts)"]
Edge --> AuthDB["MfgLiteCrystal (User, Client, RBAC)"]
Edge --> MainDB["MfgLite (Company, CompanyBranch, GstState)"]MfgLiteCrystal (Authentication & Security Database)
Feature 1: Multi-Tenant Architecture & Auth DB Setup
Feature / Change Summary
Configured the primary authentication database MfgLiteCrystal (prisma/schema.auth.prisma) containing tenant registration, module assignment, role mapping, user accounts, and subscription tiers.
Database Tables & Purpose Summaries
client(Client): Stores master multi-tenant client profile records (Client ID, name, active status).linkclientmodule(LinkClientModule): Maps which ERP feature modules (e.g. Sales, Finance, System Tools007) are purchased and enabled for a specific tenant client.linkusermodulerole(LinkUserModuleRole): Maps specific user IDs to module codes (modulecode) and role permission levels (rolecodee.g.001-006).clientsubscription(ClientSubscription): Tracks tenant subscription plans, purchased license seat counts, and plan expiry dates.users(User): Manages user credentials, active session UUIDs, SHA-256 hashed OTPs, OTP expiration timestamps, and failed attempt counters.
Prisma Models (prisma/schema.auth.prisma)
model Client {
id Int @id @default(autoincrement())
clientid String? @db.NVarChar(6)
name String? @db.NVarChar(50)
isactive String? @db.NVarChar(1)
@@index([clientid])
@@map("client")
}
model LinkClientModule {
id Int @id @default(autoincrement())
clientid String? @db.NVarChar(6)
modulecode String? @db.NVarChar(3)
@@index([clientid])
@@map("linkclientmodule")
}
model LinkUserModuleRole {
id Int @id @default(autoincrement())
clientid String? @db.NVarChar(6)
userid Int?
modulecode String? @db.NVarChar(3)
rolecode String? @db.NVarChar(3)
@@index([clientid, userid])
@@map("linkusermodulerole")
}
model ClientSubscription {
id Int @id @default(autoincrement())
clientid String? @db.NVarChar(6)
plancode String? @db.NVarChar(3)
purchasedlicenses Int?
subscriptionstartdate DateTime? @db.DateTime
subscriptionexpirydate DateTime? @db.DateTime
@@index([clientid])
@@map("clientsubscription")
}
model User {
id Int @id @default(autoincrement())
clientid String? @db.NVarChar(6)
name String? @db.NVarChar(200)
mobile String? @db.NVarChar(20)
email String? @db.NVarChar(200)
isactive String? @db.NVarChar(1)
currentsessionid String? @db.NVarChar(200)
sessionexpiresat DateTime? @db.DateTime
otpcode String? @db.NVarChar(200)
otpexpiresat DateTime? @db.DateTime
otpattempts Int @default(0)
@@index([clientid])
@@index([email])
@@index([mobile])
@@map("users")
}Feature 2: SHA-256 Hashed OTP Authentication & Brute-Force Lockout
Feature / Change Summary
Replaced plain-text OTP generation with cryptographically secure crypto.randomInt 6-digit OTP generation, SHA-256 password hashing, atomic single-transaction verification, constant-time hash comparison (crypto.timingSafeEqual), and a 5-failed-attempt lockout threshold. Added sliding-window IP rate limiting (3 requests / 60 seconds) and AWS SES email dispatch.
Functional / Workflow Changes
- Users enter their Client ID and their registered Email address or 10-digit Mobile number to identify their account.
- The database searches for a matching user record by
clientidand eitheremailormobile. - Once verified, the 6-digit OTP is generated and dispatched exclusively to the user’s registered Email address via AWS SES.
- OTPs expire after 3 minutes.
- Entering 5 consecutive incorrect OTPs locks verification attempts until a new OTP is requested.
- IP rate limiter prevents bot spamming by blocking >3 OTP requests within a 60-second window.
Files / Components Affected or Added
src/app/api/auth/send-otp/route.tssrc/app/api/auth/verify-otp/route.tssrc/app/login/page.tsxlib/email.ts
API / Server-Side Changes
POST /api/auth/send-otp: Validates Zod input (clientId6-digit regex,identifieremail), applies sliding IP rate limit, generates SHA-256 hashed OTP, and sends email via AWS SES client singleton.POST /api/auth/verify-otp: Validates Zod input, checksotpattempts >= 5, performs constant-time hash comparison (crypto.timingSafeEqual), incrementsotpattemptson failure, and clears OTP / sets session atomically inside a Prisma$transaction.
Important Validations / Business Rules
- OTP code is stored only as a SHA-256 hex digest (
NVARCHAR(200)). - OTP input string comparison uses
crypto.timingSafeEqualto prevent side-channel timing leaks. - Rate limit: Max 3 OTP generation requests per minute per IP address.
- Lockout:
otpattempts >= 5blocks verification returning429 Too Many Requests.
Feature 3: Single Session Enforcement & Session Takeover Detection
Feature / Change Summary
Implemented single active session enforcement per user. If a user logs in on a second device, the active session ID in the database is rotated using crypto.randomUUID(). The original device detects the takeover during polling and presents a “Session Taken Over” modal dialog.
Functional / Workflow Changes
- System issues 24-hour absolute session tokens with rolling 30-minute idle activity refresh (
lastActivityAt). - Second device login prompts user: “An active session was found on another device. Do you want to take over this session?”
- If confirmed, the original session is invalidated, and the original browser displays a modal blocking UI access until the user clicks “Sign In Again”.
- Expiry or revocation explicitly deletes browser cookies using
maxAge: 0andpath: '/'.
Files / Components Affected or Added
lib/constants.ts(Edge-safeSESSION_COOKIE_NAME)lib/session.ts(createSession,getSessionDetail,clearSession)components/auth-provider.tsx(60s session polling + 5svisibilitychangedebounce +AbortControllertimeout)src/middleware.ts
API / Server-Side Changes
GET /api/auth/session: ReturnssessionTakenOver: truewith status401whenuser.currentsessionid !== decoded.sessionId.lib/session.ts:clearSession()explicitly setsSet-Cookie: mfglite_session=; Max-Age=0; Path=/to purge stale browser cookies.
Feature 4: Role-Based Access Control (RBAC) & Tenant Scope Gating
Feature / Change Summary
Integrated role-based permission evaluation using module role codes stored in linkusermodulerole mapped against client modules in linkclientmodule.
Roles Introduced or Modified
| Role Code | Role Name | System Authority / Scope |
|---|---|---|
001 |
SYSTEM / System Admin |
Full multi-tenant context control (clientId: '000000'). Ability to switch active client view via /api/auth/switch-client. |
002 |
ADMIN |
Full CRUD administrative access for tenant organization masters (Company, CompanyBranch). |
003 / 004 |
MANAGER / Supervisor |
Create, view, update, and authorize operations for assigned modules. |
005 |
STAFF |
Standard operational creation and editing within assigned modules. |
006 |
READONLY |
Read-only inspection access; action buttons (Create/Modify) hidden in UI. |
Features / Pages / Actions Shown or Restricted Based on Roles
- System Tools (
/systemtools/company):- Visible only to users assigned module
007(or System Admin001). - Main Level: Displays Index list, Details view, and Modify view.
- Company Creation: Hidden from main level per enterprise workflow requirements.
- Visible only to users assigned module
- Client Switcher (
/api/auth/switch-client):- Restrictive API guard: Returns
403 ForbiddenunlessdbUser.clientid === '000000'. Target client must exist and haveisactive === 'Y'.
- Restrictive API guard: Returns
MfgLite (Main ERP Database)
Feature 5: Multi-Tenant Tenant Isolation & Company Branch Management
Feature / Change Summary
Hardened multi-tenant data isolation across Company, CompanyBranch, and GstState repositories and services. Enforced tenant-scoped validation, transactional unitCode sequence generation, and input sanitization (numeric-only pincodes).
Functional / Workflow Changes
- All Server Actions automatically extract
session.clientIdfrom server-side JWT claims (never trusting client-submittedclientidarguments). - Branch
unitCodesequence (e.g.'01','02') is automatically computed inside a single Prisma$transactionordering byunitCode: 'desc'. - Pincode inputs enforce a 6-digit numeric regex (
/^\d{6}$/) with auto-sanitization (e.target.value.replace(/\D/g, '')).
Files / Components Affected or Added
prisma/schema.prismarepositories/company/company.repository.tsrepositories/company-branch/company-branch.repository.tsservices/company/company.service.tsservices/company-branch/company-branch.service.tssrc/app/actions/company.tssrc/app/actions/company-branch.tssrc/app/systemtools/company/[id]/page.tsxlib/dto.ts(PrismaDecimal/Datesafe plain object serialization)
API / Server-Side Changes
CompanyRepository&CompanyBranchRepository:updateanddeleteexecutefindFirst({ where: { id, clientid } })validation prior to executing primary key operations (where: { id }).- Server Actions map Prisma
P2002unique constraint violations to user-friendly409 Conflicterror messages (e.g. “A company with this panno, companyemail already exists”). - Pagination parameters cap
limitto a maximum of 50 items (Math.min(Math.max(limit, 1), 50)).
Database Tables & Purpose Summaries (MfgLite)
Company(Company): Stores core tenant company profile information (PAN, email, signatory, shift hours).CompanyBranch(CompanyBranch): Stores manufacturing plant/unit branch details (GSTIN, unit code, state code, registered office flag, notes).GstState(GstState): Master table for GST state codes and state names used during branch creation.
Prisma Models (prisma/schema.prisma)
model Company {
id Int @id @default(autoincrement())
clientid String @db.NVarChar(6)
companyname String @db.NVarChar(50)
panno String @db.NChar(10)
companyemail String @db.NVarChar(50)
SignatoryType String? @db.NVarChar(50)
Signatoryname String? @db.NVarChar(100)
AuditTrail String? @db.NVarChar(1)
shifthrs Decimal @db.Decimal(4, 2)
branches CompanyBranch[]
@@unique([clientid, panno])
@@unique([clientid, companyemail])
@@index([clientid])
@@map("Company")
}
model CompanyBranch {
id Int @id @default(autoincrement())
clientid String @db.NVarChar(6)
companyId Int
gstin String @db.NVarChar(20)
homeStateCode String @db.NChar(2)
unitCode String @db.NChar(2)
unitName String @db.NVarChar(20)
isActive String @db.NChar(1)
addline1 String @db.NVarChar(100)
pincode String @db.NChar(6)
isRegdOffice String @db.NVarChar(1)
notes String? @db.NVarChar(250)
company Company @relation(fields: [companyId], references: [id], onDelete: Cascade)
@@unique([clientid, unitCode])
@@index([clientid])
@@index([companyId])
@@index([clientid, isActive])
@@map("CompanyBranch")
}
model GstState {
id Int @id @default(autoincrement())
stateCode String @db.NChar(2)
stateName String @db.NVarChar(50)
isActive String @db.NChar(1)
@@index([stateCode])
@@map("GstState")
}🎨 UI & Design Enhancements
Feature 6: Dark Mode UI & Theme Hydration Safety
Feature / Change Summary
Integrated dark mode across main layouts, system tools pages, and authentication pages (/login). Corrected SSR hydration script evaluation to prevent dark screen loading hangs.
Functional / Workflow Changes
- Theme toggle in
/settingsupdates root<html>class and persists choice inlocalStorage.setItem('theme', 'dark' | 'light'). - Root layout includes Edge-safe inline script initializing theme before render.
- Login page (
src/app/login/page.tsx) features full dark mode styling (dark:bg-slate-950,dark:border-slate-800,dark:text-white).
Files / Components Affected or Added
src/app/layout.tsxsrc/app/login/page.tsxsrc/app/settings/page.tsxsrc/index.css
📦 Build & Dependency Summary
Package & Generated Client Updates
package.json: Updatedpostinstallscript to generate both Prisma clients automatically on build:"scripts": { "postinstall": "prisma generate --schema=prisma/schema.prisma && prisma generate --schema=prisma/schema.auth.prisma" }.gitignore: Addedsrc/generated/to prevent committing platform-native binaries (query_engine-windows.dll.node).- Prune Unused Packages: Removed unused
framer-motiondependency (~45KB gzipped) and moved@types/jsonwebtokentodevDependencies.