Commit Graph

6 Commits

Author SHA1 Message Date
Claude
387e10b2fb fix(auth/middleware): recognize chunked session cookies
NextAuth v5 chunks the session cookie when the JWT payload exceeds
~4KB (we hit this easily: keycloakId + tenantId + display names +
roles + accessToken JWT + idToken). When chunked, the bare
'authjs.session-token' cookie is removed in favour of
'authjs.session-token.0', '.1', etc. Looking up only the bare name
returned undefined and the middleware redirected freshly-logged-in
users back to /api/auth/signin in a loop.

Match presence-only on any cookie whose name starts with either
canonical prefix. Server-side NextAuth still validates the token on
every RSC render — this check only gates the redirect.

Repro: gscCRM/v2.3.3 with the proxy fix in place. Keycloak auth
completes, /api/auth/callback/keycloak 302s, but every subsequent
request to / 307s straight back to signin because the middleware
doesn't see the now-chunked cookie.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 11:46:01 +02:00
Claude
360b611ae6 fix(auth): default signInPath to /api/auth/signin (NextAuth v5)
Provider-specific paths like /api/auth/signin/keycloak are POST only
in NextAuth v5 — they're the form-submit endpoint with CSRF. A GET
redirect there bounces to /api/auth/error?error=Configuration with
"UnknownAction".

/api/auth/signin (no provider segment) is the GET-accessible page
that lists configured providers. Apps that want one-click Keycloak
should set signInPath to a custom page that calls signIn('keycloak').

Repros against next-auth 5.0.0-beta.31 on Next 16.1.1. Pre-existing
bug in createAuth + createAuthMiddleware + signInRedirect; surfaced
when first user-driven login was attempted against the live CRM.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:48:50 +02:00
Claude
b0e2c21d0a fix(auth): widen AuthBundle.handlers to (Request) => Promise<Response>
NextAuthResult["handlers"] embeds NextRequest from the kit's own
next/node_modules copy, which conflicts with the consumer's next and
produces a spurious RouteHandlerConfig mismatch in
.next/types/validator.ts for every app's [...nextauth]/route.ts.

Replace the inferred type with a structural AuthRouteHandlers shape
using web-standard Request/Response. NextRequest extends Request, so
function-parameter contravariance makes this assignable wherever
Next's validator wants (request: NextRequest, ctx) => ...

Cast through unknown at the return site since TS can't prove the
contravariance across the two next copies on its own.

Verified: gscCRM `npm run build` clean, `tsc --noEmit` no longer
flags validator.ts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:35:08 +02:00
Claude
d430680df5 feat: v0.3.0 — Phase 3/4 façades + AppLayout on AppShell
- Curated re-exports from @limitless/ui through /forms, /data,
  /feedback, /navigation, /utils sub-paths so apps stop importing
  from the lower layer.
- /forms also re-exports the full @limitless/ui validation surface
  (hooks, format/security/address validators, types).
- AppLayout is now a thin wrapper over @limitless/ui's <AppShell> —
  same ShellConfig DTO, no duplicated chrome code.
- shell/types + shell/index re-export from @limitless/ui to keep one
  canonical type and one shared context.
- auth middleware: loose NextRequestLike typing to avoid two-copies-
  of-next conflict with the consumer's next.
- postbuild: rewrite ../images/ to ./images/ in copied CSS so refs
  resolve in dist/styles/.

Widget family in /data is the intersection of limitless's two
Widget files (Widget.d.ts vs Widget/index.d.ts collision in dist);
upstream fix needed before exposing IconWidget/UserWidget/etc.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 08:26:30 +02:00
Claude
1f2141118d feat: Phase 2 — layout · auth · shell are real
@gsc/web-kit v0.2.0. Three modules turn from stubs into the working
surface apps need to render a chrome-wrapped Next.js page with one
import per concern.

auth/server:
- createAuth({ keycloak: { clientId, clientSecret, issuer } }) factory
  returns { handlers, signIn, signOut, auth, requireAuth, signInPath }.
  Canonical SessionUser shape (id, keycloakId, tenantId, email,
  displayName, givenName, familyName, roles, accessToken, idToken)
  baked into the session callback. Apps drop their hand-rolled
  src/auth.ts (~80 lines) for a 6-line factory call.
- requireAuth() — server-only. await it at the top of an RSC layout
  or page; redirects to signInPath if no session.

auth/middleware:
- createAuthMiddleware({ publicRoutes? }) returns a Next.js middleware
  that redirects unauth'd requests to /api/auth/signin/keycloak with
  ?callbackUrl=<original>. Bypasses /api/auth/*, /_next/*, /images/*,
  favicon, robots.txt always.

auth (client):
- signInRedirect(callbackUrl?) — hard-nav from any client component.

shell/server:
- fetchShellConfig({ appKey, accessToken, apiUrl?, timeoutMs? }).
  Server-only fetcher. 3s default timeout. Graceful fallback config
  on any error — shell-api outages can't blank-screen a host app.

shell (client):
- <ShellProvider> + useShell() — read the resolved config from any
  descendant of <AppLayout>.

layout:
- <AppLayout config currentPath translate onSignOut navbarExtras>.
  Renders the chronos-style Bootstrap-Layout-3 chrome (navbar-static,
  sidebar-light sidebar-main with collapse + persistence in
  localStorage, navbar-footer). Wraps children with the kit's
  ShellProvider so useShell() works.

devDep: @types/node for the server-side process.env read.

All 14 sub-exports still resolve under dist/. Phase 3 (data + forms)
and the gscCRM pilot cutover come next.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 00:20:08 +02:00
Claude
957880e5c5 feat: @gsc/web-kit v0.1.0 — Phase 1 scaffold
The kit. Drop into any GSC Next.js frontend; everything that's not
domain content lives here. Wraps @limitless/ui primitives with the
app-shaped patterns we keep reimplementing: layout, auth, data
display, forms, feedback, navigation.

Phase 1 ships the package skeleton:

- package.json with 14 sub-exports (./layout · ./auth · ./auth/server
  · ./auth/middleware · ./shell · ./shell/server · ./data · ./forms ·
  ./feedback · ./navigation · ./api · ./utils + the root and ./css).
- Empty module stubs so the import map resolves while later phases
  fill in real surface area.
- Canonical CSS bundle at @gsc/web-kit/css — all.min.css +
  sidebar-overrides.css + the seven layout-3 background images,
  copied from chronos and committed in one place so no app has to
  ship the 1MB sidecar on its own anymore.
- tsc-based build + a postbuild script that mirrors @limitless/ui:
  emits .js + .d.ts, copies styles/, rewrites bare ESM imports to
  include .js extensions.
- Peer deps on next, react, react-dom, bootstrap.
- Hard deps on @limitless/ui (file: dep), next-auth, next-intl, zod.

Build verified: tsc emits, all 14 export paths resolve under dist/.
No functional code yet — Phase 2 lands AppLayout / createAuth /
fetchShellConfig and the gscCRM pilot cuts over.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-11 00:09:36 +02:00