From 339ae47540d82a8f77f0cec78c53ba5136c880c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hartmut=20N=C3=B6renberg?= Date: Fri, 3 Apr 2026 10:42:31 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20learning=20=E2=80=94=20Auth.js=20v5=20E?= =?UTF-8?q?dge=20split=20+=20session=20expiry=20redirect=20pattern?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LEARNINGS.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/LEARNINGS.md b/LEARNINGS.md index 45ce680..0f72426 100644 --- a/LEARNINGS.md +++ b/LEARNINGS.md @@ -7,6 +7,42 @@ ## Learnings +### 2026-04-03 | Auth | Session expiry redirect — Auth.js v5 + Edge runtime split + +**Problem:** Auth.js `authorize()` callback uses `@node-rs/argon2` (native module, not Edge-compatible). Using `auth()` directly in `middleware.ts` would pull argon2 into the Edge bundle and crash. + +**Solution — split config pattern:** +- `auth.config.ts` — edge-safe subset: `pages`, `session`, `cookies`, no providers, no callbacks that touch DB or argon2 +- `auth-edge.ts` — `NextAuth(authConfig)` with the lean config; used only by middleware +- `auth.ts` — spreads `authConfig`, adds Credentials provider + argon2 callbacks + prisma session tracking + +**Middleware wrapping:** +```ts +import { auth } from "./server/auth-edge.js"; +export default auth(function middleware(request) { + if (!isPublicPath(pathname) && !request.auth) { + return NextResponse.redirect(new URL("/auth/signin", request.url)); + } + // CSP logic... +}); +``` + +**Three-layer defence:** +1. Middleware — server-side redirect before page renders +2. `SessionGuard` client component — `useSession()` → `router.replace()` on SPA navigation +3. `QueryCache` / `MutationCache` in TRPCProvider — UNAUTHORIZED tRPC errors → `window.location.replace()` + +**Test mock pattern for middleware tests:** +```ts +vi.mock("./server/auth-edge.js", () => ({ + auth: (handler) => (req) => + handler(Object.assign(req, { auth: { user: { id: "test-user" } } })), +})); +``` +Needed because `vi.resetModules()` inside the helper function doesn't re-apply top-level mocks — always declare `vi.mock(...)` at file scope. + +--- + ### 2026-04-02 | DevOps | Gitea API token location **Token:** `~/.gitea-token` (chmod 600, never committed to repo)