From 12b3ad9ef36f4e28edcfe08e4520a41a3a871b24 Mon Sep 17 00:00:00 2001 From: Chris Farhood Date: Thu, 14 May 2026 18:58:40 +0000 Subject: [PATCH] fix(GRO-1272): auto-provision staff record on first OIDC login When a user authenticates via OIDC but has no staff record (userId NULL, oidcSub mismatch, email mismatch), resolveStaffMiddleware now checks for a Better-Auth user record by jwt.sub and auto-creates a minimal groomer staff record on first login. This fixes the UAT regression where all API routes returned 403 for all authenticated users after GRO-1207, because seedKnownUsers() sets oidcSub to Authentik integer PKs or emails rather than the actual Authentik OIDC sub (a UUID). The auto-provision path bridges the gap for all UAT personas without requiring seed/Terraform changes. Co-Authored-By: Paperclip --- src/middleware/rbac.ts | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/middleware/rbac.ts b/src/middleware/rbac.ts index 1277b2c..222442e 100644 --- a/src/middleware/rbac.ts +++ b/src/middleware/rbac.ts @@ -1,5 +1,5 @@ import type { MiddlewareHandler } from "hono"; -import { and, eq, getDb, sql, staff } from "@groombook/db"; +import { and, eq, getDb, sql, staff, user } from "@groombook/db"; export type StaffRole = "groomer" | "receptionist" | "manager"; export type StaffRow = typeof staff.$inferSelect; @@ -110,6 +110,30 @@ export const resolveStaffMiddleware: MiddlewareHandler = async ( return; } } + // Auto-provision: no staff record exists for this user at all, but a valid + // Better-Auth user session exists (jwt.sub = user.id from user table). + // Create a minimal groomer staff record on first login. + const [userRow] = await db + .select({ id: user.id, name: user.name, email: user.email }) + .from(user) + .where(eq(user.id, jwt.sub)) + .limit(1); + if (userRow) { + const [newStaff] = await db + .insert(staff) + .values({ + name: userRow.name ?? jwt.email?.split("@")[0] ?? "Unknown", + email: userRow.email ?? jwt.email ?? "", + userId: jwt.sub, + role: "groomer", + isSuperUser: false, + active: true, + }) + .returning(); + c.set("staff", newStaff); + await next(); + return; + } return c.json( { error: "Forbidden: no staff record found for authenticated user" }, 403