RMR Group
Dual-app monorepo for a microfinance organization serving women across India. Public website + internal CMS for loan management with JWT auth, Prisma ORM, and full type safety.
Overview
RMR Group is a monorepo microfinance platform with two Next.js apps: a CMS (Collection Management System) for internal staff, and a public-facing web app. The CMS handles loan origination, daily payment collection tracking, and agent management for a business where field agents collect small daily installments from borrowers across India.
Why it was built
A client project for a real microfinance operation that was running entirely on spreadsheets. The risks were obvious: no audit trail, no role separation, easy to manipulate figures. The brief was to replace that with a purpose-built internal tool with proper auth, validation, and structured data, starting with Sprint 1 (auth) and building toward full loan lifecycle management.
RS256 Asymmetric JWT Auth
Login issues a JWT signed with a private key. All requests and Next.js middleware verify using only the public key. The CMS never holds the signing key. Keys are base64-encoded PEM strings in env vars, generated by a utility script in the repo.
Role-Based Middleware
Next.js middleware reads the JWT cookie on every request. Routes under /admin/ pass only for SUPER_ADMIN. API routes return 403 for agents. User id, role, and email are forwarded as request headers to downstream handlers.
IP Rate Limiting
An in-memory Map keyed by login:{IP} allows 5 attempts per 15-minute window. Returns Retry-After header with 429 responses. No Redis needed: it resets automatically when the window expires.
Tech Breakdown
Three shared packages (db, validators, types) consumed by both apps. Turborepo's pipeline caching makes this practical: one change to the validators package rebuilds only what depends on it.
The database client, all service functions, and the Prisma schema live in one shared package. This prevents auth logic from diverging between the two apps: hashPassword and verifyJWT are defined once.
Asymmetric signing means the public-facing app can verify tokens without ever holding the private signing key. A meaningful security decision for a multi-app setup handling financial data.
Schemas are both the validation logic and the TypeScript type source via z.infer<>. CreateLoanInput is derived from the schema, not written separately. One source of truth.
Production-grade testing from Sprint 1. Auth is the highest-risk piece: getting it wrong means financial data exposure. Tests run before any code ships.
- 1
RS256 key pair setup in a Next.js project is non-trivial. Keys are base64-encoded, stored in env vars, decoded and parsed by the jose library on every middleware request. A generate-keys.js utility script exists specifically to help with onboarding.
- 2
Role-based routing in Next.js middleware requires handling both API routes (JSON 403) and page routes (redirect to /login) as separate code paths, since getting the response type wrong breaks either the frontend or the API client.
Sprint 1 (auth) is complete. Sprint 2 is customer and loan CRUD: the Zod schemas are already written, the API routes and UI are next. After that: payment collection dashboard, agent performance reports, and the public web app.
Want to know more?
Happy to walk you through the details, just reach out.