This repository has been archived on 2026-05-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
app/apps/api/src/middleware/portalSession.ts
T
groombook-engineer[bot] 2577e33c50 feat(GRO-653): add portal session middleware and server-side audit logging (#300)
* feat(GRO-653): add portal session middleware and server-side audit logging

- Add validatePortalSession middleware that reads X-Impersonation-Session-Id header,
  queries impersonationSessions, and sets portalClientId + portalSessionId on the context
- Add portalAudit middleware that logs all portal requests to impersonationAuditLogs table
- Apply both middlewares to the portalRouter
- Replace all getClientIdFromSession() calls with c.get("portalClientId")
- Remove getClientIdFromSession() helper and inline session checks in waitlist routes
- Consistent session.expiry > new Date() check across all routes

Co-Authored-By: Paperclip <noreply@paperclip.ing>

* fix(GRO-653): remove unused sessionId variable and and import

Fix lint errors flagged by QA:
- Remove unused `sessionId` variable from PATCH waitlist handler
- Remove unused `and` import from portal.ts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Paperclip <noreply@paperclip.ing>

---------

Co-authored-by: Flea Flicker <fleaflicker@groombook.farh.net>
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Flea Flicker <flea-flicker@groombook.ai>
2026-04-16 11:20:36 +00:00

41 lines
1.3 KiB
TypeScript

import type { MiddlewareHandler } from "hono";
import { and, eq, getDb, impersonationSessions } from "@groombook/db";
export interface PortalEnv {
Variables: {
portalClientId: string;
portalSessionId: string;
};
}
/**
* Validates the X-Impersonation-Session-Id header against the impersonationSessions table.
* Must be applied to all portal routes.
*
* Reads x-session-id from request headers, queries impersonationSessions for a row where
* id = sessionId AND status = 'active', and checks session.expiresAt > new Date().
* Returns 401 if session is invalid/missing/expired.
* On success, sets c.set("portalClientId", session.clientId) and c.set("portalSessionId", session.id).
*/
export const validatePortalSession: MiddlewareHandler<PortalEnv> = async (c, next) => {
const sessionId = c.req.header("X-Impersonation-Session-Id");
if (!sessionId) {
return c.json({ error: "Unauthorized" }, 401);
}
const db = getDb();
const [session] = await db
.select()
.from(impersonationSessions)
.where(and(eq(impersonationSessions.id, sessionId), eq(impersonationSessions.status, "active")))
.limit(1);
if (!session || session.expiresAt <= new Date()) {
return c.json({ error: "Unauthorized" }, 401);
}
c.set("portalClientId", session.clientId);
c.set("portalSessionId", session.id);
await next();
};