fix: CSRF host-compare behind proxy + default port = creation order
All checks were successful
Build & Push Docker Images / lint (push) Successful in 3m1s
Build & Push Docker Images / build-and-push (push) Successful in 7m30s

Two prod-only breakages found after go-live:

1. CSRF guard rejected EVERY /api/v1 mutation ("Cross-origin state-changing
   request rejected", 403) — making the CRM read-only. It compared the
   browser Origin (https://crm.portnimara.com) against request.nextUrl.origin,
   but TLS terminates at nginx so the app sees http://127.0.0.1 → protocol
   mismatch. Compare hosts instead (Host header survives the proxy; a
   cross-site attacker can't forge the browser-set Origin host).

2. Post-login landed on port-amador (empty tenant), not port-nimara. Three
   queries ordered ports by name (alphabetical → Amador first): the bare
   /dashboard redirect (app/dashboard/page.tsx), the dashboard layout's
   defaultPortId, and /api/v1/me/ports. Order by createdAt so the primary
   (first-seeded) port — Port Nimara — leads, matching listPorts().

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-03 03:38:12 +02:00
parent 23a5811342
commit d485695357
4 changed files with 18 additions and 11 deletions

View File

@@ -38,7 +38,7 @@ export default async function DashboardLayout({ children }: { children: React.Re
});
const ports = profile?.isSuperAdmin
? await db.query.ports.findMany({ orderBy: portsTable.name })
? await db.query.ports.findMany({ orderBy: portsTable.createdAt })
: portRoles.map((pr) => pr.port);
// Prefer a previously-resolved tier from the client's cookie so the

View File

@@ -39,7 +39,7 @@ export async function GET() {
if (profile.isSuperAdmin) {
const all = await db.query.ports.findMany({
where: eq(portsTable.isActive, true),
orderBy: portsTable.name,
orderBy: portsTable.createdAt,
columns: { id: true, slug: true, name: true },
});
return NextResponse.json({ data: all });

View File

@@ -47,7 +47,7 @@ export default async function DashboardRedirectPage() {
if (!slug) {
if (profile?.isSuperAdmin) {
const first = await db.query.ports.findFirst({ orderBy: portsTable.name });
const first = await db.query.ports.findFirst({ orderBy: portsTable.createdAt });
slug = first?.slug;
} else {
const role = await db.query.userPortRoles.findFirst({