new-site/api/src/middleware/customer-auth.ts
justin f8cd37ac8c Initial commit — Performance West telecom compliance platform
Includes: API (Express/TypeScript), Astro site, Python workers,
document generators, FCC compliance tools, Canada CRTC formation,
Ansible infrastructure, and deployment scripts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 06:54:22 -05:00

57 lines
1.6 KiB
TypeScript

import { Request, Response, NextFunction } from "express";
import jwt from "jsonwebtoken";
const JWT_SECRET = process.env.ADMIN_JWT_SECRET || "changeme";
const COOKIE_NAME = "pw_customer";
export interface CustomerPayload {
customerId: number;
email: string;
}
declare global {
namespace Express {
interface Request {
customer?: CustomerPayload;
}
}
}
/** Middleware: attach customer from cookie JWT. Never blocks — sets req.customer if valid. */
export function optionalCustomerAuth(req: Request, _res: Response, next: NextFunction) {
const token = req.cookies?.[COOKIE_NAME];
if (!token) return next();
try {
req.customer = jwt.verify(token, JWT_SECRET) as CustomerPayload;
} catch {
// expired or invalid — ignore, let route decide
}
next();
}
/** Middleware: require valid customer session. Returns 401 if not logged in. */
export function requireCustomerAuth(req: Request, res: Response, next: NextFunction) {
optionalCustomerAuth(req, res, () => {
if (!req.customer) {
return res.status(401).json({ error: "Login required", code: "UNAUTHENTICATED" });
}
next();
});
}
/** Issue a customer session JWT cookie (7-day). */
export function issueCustomerCookie(res: Response, payload: CustomerPayload) {
const token = jwt.sign(payload, JWT_SECRET, { expiresIn: "7d" });
res.cookie(COOKIE_NAME, token, {
httpOnly: true,
secure: process.env.NODE_ENV === "production",
sameSite: "lax",
maxAge: 7 * 24 * 60 * 60 * 1000,
path: "/",
});
}
/** Clear the customer session cookie. */
export function clearCustomerCookie(res: Response) {
res.clearCookie(COOKIE_NAME, { path: "/" });
}