fix(rbac): fallback lookup for staff records predating Better-Auth userId

GRO-153: /api/staff returned 403 for all staff because resolveStaffMiddleware
looked up by staff.userId (Better-Auth ID) but dev login sent staff.id (PK),
and existing staff records had userId=NULL.

Changes:
- resolveStaffMiddleware: try userId first, fall back to staff.id (dev mode)
- resolveStaffMiddleware: try userId first, fall back to oidcSub (production)
- GET /api/dev/users: include userId field for DevLoginSelector
- DevLoginSelector: send userId (not staff.id) as X-Dev-User-Id
- Migration 0018: backfill userId for known demo staff

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
groombook-engineer[bot]
2026-03-28 01:48:25 +00:00
parent e68d5a2594
commit 57e9670410
5 changed files with 50 additions and 6 deletions
+26 -5
View File
@@ -40,18 +40,29 @@ export const resolveStaffMiddleware: MiddlewareHandler<AppEnv> = async (
await next();
return;
}
// Treat X-Dev-User-Id as the Better-Auth user ID
// Treat X-Dev-User-Id as the Better-Auth user ID first
const [row] = await db
.select()
.from(staff)
.where(eq(staff.userId, devUserId));
if (!row) {
if (row) {
c.set("staff", row);
await next();
return;
}
// Fallback: if userId is null, treat X-Dev-User-Id as staff.id (dev login
// may send the primary key for staff records that predate the userId field)
const [fallbackRow] = await db
.select()
.from(staff)
.where(eq(staff.id, devUserId));
if (!fallbackRow) {
return c.json(
{ error: "Forbidden: no staff record found for X-Dev-User-Id" },
403
);
}
c.set("staff", row);
c.set("staff", fallbackRow);
await next();
return;
}
@@ -61,13 +72,23 @@ export const resolveStaffMiddleware: MiddlewareHandler<AppEnv> = async (
.select()
.from(staff)
.where(eq(staff.userId, jwt.sub));
if (!row) {
if (row) {
c.set("staff", row);
await next();
return;
}
// Fallback: staff records that predate the userId field may still have oidcSub
const [fallbackRow] = await db
.select()
.from(staff)
.where(eq(staff.oidcSub, jwt.sub));
if (!fallbackRow) {
return c.json(
{ error: "Forbidden: no staff record found for authenticated user" },
403
);
}
c.set("staff", row);
c.set("staff", fallbackRow);
await next();
};