Files
pos/server/src/middleware/auth.ts
jason d53c772dd6 Add Milestones 1 & 2: full-stack POS foundation with admin UI
- Node/Express/TypeScript API under /api/v1 with JWT auth (login, refresh, logout, /me)
- Prisma schema: vendors, users, roles, products, categories, taxes, transactions
- SQLite for local dev; Postgres via docker-compose for production
- Full CRUD routes for vendors, users, categories, taxes, products with Zod validation and RBAC
- Paginated list endpoints scoped per vendor; refresh token rotation
- React/TypeScript admin SPA (Vite): login, protected routing, sidebar layout
- Pages: Dashboard, Catalog (tabbed Products/Categories/Taxes), Users, Vendor Settings
- Shared UI: Table, Modal, FormField, Btn, PageHeader components
- Multi-stage Dockerfile; docker-compose with Postgres healthcheck
- Seed script with demo vendor and owner account
- INSTRUCTIONS.md, ROADMAP.md, .claude/launch.json for dev server config

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 23:18:04 -05:00

44 lines
1.2 KiB
TypeScript

import { Response, NextFunction } from "express";
import jwt from "jsonwebtoken";
import { AuthPayload, AuthenticatedRequest } from "../types/index.js";
import { AppError } from "./errorHandler.js";
export function requireAuth(
req: AuthenticatedRequest,
_res: Response,
next: NextFunction
): void {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith("Bearer ")) {
return next(new AppError(401, "UNAUTHORIZED", "Missing or invalid token"));
}
const token = authHeader.slice(7);
const secret = process.env.JWT_SECRET;
if (!secret) {
return next(new AppError(500, "CONFIG_ERROR", "JWT secret not configured"));
}
try {
const payload = jwt.verify(token, secret) as AuthPayload;
req.auth = payload;
next();
} catch {
next(new AppError(401, "UNAUTHORIZED", "Invalid or expired token"));
}
}
export function requireRole(...roles: string[]) {
return (req: AuthenticatedRequest, _res: Response, next: NextFunction) => {
if (!req.auth) {
return next(new AppError(401, "UNAUTHORIZED", "Not authenticated"));
}
if (!roles.includes(req.auth.roleName)) {
return next(
new AppError(403, "FORBIDDEN", "Insufficient permissions")
);
}
next();
};
}