fix(auth): use token.sid to avoid Auth.js jti claim conflict
Auth.js v5 manages token.jti internally and overwrites it after the jwt callback. Storing our session UUID in token.sid ensures the value we persist in active_sessions matches what the signed cookie carries. - jwt callback: token.sid = jti (was token.jti) - session callback: read from token.sid - signOut event: falls back to token.jti for backward compat with any sessions created before this change Also adds Playwright dev-system test suite (playwright.dev.config.ts + e2e/dev-system/) that validates login, session registry health, and RBAC enforcement against the running localhost:3100 dev server. Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
import { expect, type Page } from "@playwright/test";
|
||||
|
||||
/** Dev-system credentials — these exist in the planarchy.dev seed data */
|
||||
export const DEV_USERS = {
|
||||
admin: { email: "admin@planarchy.dev", password: "admin123" },
|
||||
manager: { email: "manager@planarchy.dev", password: "manager123" },
|
||||
viewer: { email: "viewer@planarchy.dev", password: "viewer123" },
|
||||
} as const;
|
||||
|
||||
export async function signIn(page: Page, email: string, password: string) {
|
||||
await page.goto("/auth/signin");
|
||||
await page.fill('input[type="email"]', email);
|
||||
await page.fill('input[type="password"]', password);
|
||||
await page.click('button[type="submit"]');
|
||||
await expect(page).toHaveURL(/\/(dashboard|resources)/, { timeout: 15000 });
|
||||
}
|
||||
|
||||
export async function signOut(page: Page) {
|
||||
await page.goto("/auth/signout");
|
||||
// Wait for redirect back to signin
|
||||
await page.waitForURL(/\/auth\/signin/, { timeout: 10000 });
|
||||
}
|
||||
|
||||
/**
|
||||
* Intercept all tRPC batch responses and assert none return HTTP 401.
|
||||
* Returns a list of intercepted tRPC paths that were called.
|
||||
*/
|
||||
export async function assertNoTrpc401s(page: Page, action: () => Promise<void>) {
|
||||
const failures: string[] = [];
|
||||
|
||||
page.on("response", (response) => {
|
||||
if (response.url().includes("/api/trpc/") && response.status() === 401) {
|
||||
const url = new URL(response.url());
|
||||
failures.push(url.pathname + url.search.slice(0, 80));
|
||||
}
|
||||
});
|
||||
|
||||
await action();
|
||||
|
||||
if (failures.length > 0) {
|
||||
throw new Error(
|
||||
`tRPC 401 responses detected (session registry / auth broken):\n${failures.join("\n")}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user