From b1086d14e9839242fbaf86b8c4e712b7433f620a Mon Sep 17 00:00:00 2001 From: Ajay Ghanwat Date: Tue, 11 Aug 2026 17:05:04 +0530 Subject: [PATCH] fix(auth): secure hydration and align password forms --- bun.lock | 8 ++--- examples/basic-app/app/layouts/document.wrn | 6 ++-- .../app/layouts/document.wrn | 2 ++ .../scripts/generate-showcase.mjs | 2 ++ .../i18n-showcase/app/layouts/document.wrn | 2 ++ .../apps/admin/app/layouts/document.wrn | 6 ++-- .../apps/web/app/layouts/document.wrn | 6 ++-- packages/auth/components/SignIn.wrn | 3 +- packages/auth/package.json | 2 +- packages/cli/package.json | 2 +- packages/cli/src/create.ts | 8 +++-- packages/cli/test/create.test.ts | 4 +++ packages/dev-server/package.json | 2 +- packages/dev-server/src/runtime.ts | 1 - packages/ui/components/StrongPassword.wrn | 17 +++++++--- packages/ui/components/TogglePassword.wrn | 31 ++++++++++++++++++- packages/ui/package.json | 2 +- 17 files changed, 77 insertions(+), 27 deletions(-) diff --git a/bun.lock b/bun.lock index 34807ee7..4c46e295 100644 --- a/bun.lock +++ b/bun.lock @@ -212,7 +212,7 @@ }, "packages/auth": { "name": "@wrnexus/auth", - "version": "0.8.9", + "version": "0.8.10", "dependencies": { "@wrnexus/authz": "workspace:*", "@wrnexus/captcha": "workspace:*", @@ -268,7 +268,7 @@ }, "packages/cli": { "name": "@wrnexus/cli", - "version": "0.8.12", + "version": "0.8.13", "bin": { "wrnexus": "src/index.ts", }, @@ -332,7 +332,7 @@ }, "packages/dev-server": { "name": "@wrnexus/dev-server", - "version": "0.8.11", + "version": "0.8.12", "dependencies": { "@wrnexus/authz": "workspace:*", "@wrnexus/cache": "workspace:*", @@ -617,7 +617,7 @@ }, "packages/ui": { "name": "@wrnexus/ui", - "version": "0.8.10", + "version": "0.8.11", "dependencies": { "@wrnexus/core": "workspace:*", }, diff --git a/examples/basic-app/app/layouts/document.wrn b/examples/basic-app/app/layouts/document.wrn index 3a2d0cdc..f995519b 100644 --- a/examples/basic-app/app/layouts/document.wrn +++ b/examples/basic-app/app/layouts/document.wrn @@ -1,10 +1,10 @@ // Global document layout. The framework renders this once around the selected // page layout and merges SEO metadata, styles, and scripts into /. -// Request cookies, resolved theme, language, URL, and pathname are available -// as SSR props, so document attributes do not need a client-side correction. +// Resolved theme, language, URL, and pathname are available as safe SSR props. layout Document { + runtime = "server" + props { - cookies = {} theme = "light" language = "en" url = "" diff --git a/examples/component-showcase/app/layouts/document.wrn b/examples/component-showcase/app/layouts/document.wrn index 7fd2b7d1..bbdc762b 100644 --- a/examples/component-showcase/app/layouts/document.wrn +++ b/examples/component-showcase/app/layouts/document.wrn @@ -1,5 +1,7 @@ // Generated by scripts/generate-showcase.mjs. Do not edit directly. layout Document { + runtime = "server" + props { cookies = {} theme = "light" diff --git a/examples/component-showcase/scripts/generate-showcase.mjs b/examples/component-showcase/scripts/generate-showcase.mjs index 0a43b0b3..85b404da 100644 --- a/examples/component-showcase/scripts/generate-showcase.mjs +++ b/examples/component-showcase/scripts/generate-showcase.mjs @@ -1729,6 +1729,8 @@ layout Showcase { function documentLayout() { return `// Generated by scripts/generate-showcase.mjs. Do not edit directly. layout Document { + runtime = "server" + props { cookies = {} theme = "light" diff --git a/examples/i18n-showcase/app/layouts/document.wrn b/examples/i18n-showcase/app/layouts/document.wrn index 7cbafccd..92072fe3 100644 --- a/examples/i18n-showcase/app/layouts/document.wrn +++ b/examples/i18n-showcase/app/layouts/document.wrn @@ -1,4 +1,6 @@ layout Document { + runtime = "server" + props { language: string = "en" } diff --git a/examples/inter-app-api-showcase/apps/admin/app/layouts/document.wrn b/examples/inter-app-api-showcase/apps/admin/app/layouts/document.wrn index 1aaaf5dd..924a01e0 100644 --- a/examples/inter-app-api-showcase/apps/admin/app/layouts/document.wrn +++ b/examples/inter-app-api-showcase/apps/admin/app/layouts/document.wrn @@ -1,10 +1,10 @@ // Global document layout. The framework renders this once around the selected // page layout and merges SEO metadata, styles, and scripts into /. -// Request cookies, resolved theme, language, URL, and pathname are available -// as SSR props, so document attributes do not need a client-side correction. +// Resolved theme, language, URL, and pathname are available as safe SSR props. layout Document { + runtime = "server" + props { - cookies = {} theme = "light" language = "en" url = "" diff --git a/examples/inter-app-api-showcase/apps/web/app/layouts/document.wrn b/examples/inter-app-api-showcase/apps/web/app/layouts/document.wrn index 1aaaf5dd..924a01e0 100644 --- a/examples/inter-app-api-showcase/apps/web/app/layouts/document.wrn +++ b/examples/inter-app-api-showcase/apps/web/app/layouts/document.wrn @@ -1,10 +1,10 @@ // Global document layout. The framework renders this once around the selected // page layout and merges SEO metadata, styles, and scripts into /. -// Request cookies, resolved theme, language, URL, and pathname are available -// as SSR props, so document attributes do not need a client-side correction. +// Resolved theme, language, URL, and pathname are available as safe SSR props. layout Document { + runtime = "server" + props { - cookies = {} theme = "light" language = "en" url = "" diff --git a/packages/auth/components/SignIn.wrn b/packages/auth/components/SignIn.wrn index af2e1fb3..f592e4b2 100644 --- a/packages/auth/components/SignIn.wrn +++ b/packages/auth/components/SignIn.wrn @@ -36,9 +36,10 @@ component SignIn {
- /. -// Request cookies, resolved theme, language, URL, and pathname are available -// as SSR props, so document attributes do not need a client-side correction. +// Resolved theme, language, URL, and pathname are available as safe SSR props, +// so document attributes do not need a client-side correction. Request cookies +// remain server-private and are never passed into document hydration. layout Document { + runtime = "server" + props { - cookies = {} theme = "light" language = "en" url = "" diff --git a/packages/cli/test/create.test.ts b/packages/cli/test/create.test.ts index b0a3261a..fc8aaf3f 100644 --- a/packages/cli/test/create.test.ts +++ b/packages/cli/test/create.test.ts @@ -67,6 +67,10 @@ test("scaffoldApp includes the complete v0.8 configuration and starter structure scaffoldApp(root, "complete-app"); const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8")); const config = readFileSync(join(root, "wrnexus.config.ts"), "utf8"); + const documentLayout = readFileSync(join(root, "app/layouts/document.wrn"), "utf8"); + + expect(documentLayout).toContain('runtime = "server"'); + expect(documentLayout).not.toContain("cookies ="); for (const packageName of [ "@wrnexus/auth", diff --git a/packages/dev-server/package.json b/packages/dev-server/package.json index 25f8855a..e43b0efc 100644 --- a/packages/dev-server/package.json +++ b/packages/dev-server/package.json @@ -1,6 +1,6 @@ { "name": "@wrnexus/dev-server", - "version": "0.8.11", + "version": "0.8.12", "type": "module", "main": "src/index.ts", "exports": { diff --git a/packages/dev-server/src/runtime.ts b/packages/dev-server/src/runtime.ts index 009e4704..dc31eeb6 100644 --- a/packages/dev-server/src/runtime.ts +++ b/packages/dev-server/src/runtime.ts @@ -1785,7 +1785,6 @@ export function createHandlers(deps: RuntimeDeps): Handlers { if (typeof documentRender === "function") { const rendered = String( documentRender({ - cookies: ctx.cookies.getAll(), theme: resolvedTheme, language, url: ctx.url.toString(), diff --git a/packages/ui/components/StrongPassword.wrn b/packages/ui/components/StrongPassword.wrn index 729a4c83..ca1ed639 100644 --- a/packages/ui/components/StrongPassword.wrn +++ b/packages/ui/components/StrongPassword.wrn @@ -174,7 +174,7 @@ component StrongPassword { {#if requireLowercase}
  • One lowercase letter
  • {/if} {#if requireUppercase}
  • One uppercase letter
  • {/if} {#if requireNumber}
  • One number
  • {/if} - {#if requireSpecialCharacter}
  • One special character: {specialCharactersSet}
  • {/if} + {#if requireSpecialCharacter}
  • One special character: {specialCharactersSet}
  • {/if} {/if} {#if hintText}{hintText}{/if} @@ -205,7 +205,7 @@ component StrongPassword { {#if requireLowercase}
  • One lowercase letter
  • {/if} {#if requireUppercase}
  • One uppercase letter
  • {/if} {#if requireNumber}
  • One number
  • {/if} - {#if requireSpecialCharacter}
  • One special character: {specialCharactersSet}
  • {/if} + {#if requireSpecialCharacter}
  • One special character: {specialCharactersSet}
  • {/if} {/if} {#if hintText}{hintText}{/if} @@ -236,8 +236,8 @@ component StrongPassword { .wire-next--strong-password input { width: 100%; - min-height: 2.75em; - padding: 0.68em 0.85em; + min-height: 2.65rem; + padding: 0.7rem 0.85rem; border: 1px solid var(--wire-color-border); border-radius: var(--wire-radius-sm); outline: 0; @@ -322,6 +322,15 @@ component StrongPassword { line-height: 1.45; } + .wire-next__strong-password-requirements .wire-next__strong-password-special { + grid-column: 1 / -1; + white-space: nowrap; + } + + .wire-next__strong-password-special [data-text="specialCharactersSet"] { + white-space: nowrap; + } + .wire-next__strong-password-requirements li > span[aria-hidden="true"] { position: relative; width: 0.9rem; diff --git a/packages/ui/components/TogglePassword.wrn b/packages/ui/components/TogglePassword.wrn index 2f5c015b..0aecac65 100644 --- a/packages/ui/components/TogglePassword.wrn +++ b/packages/ui/components/TogglePassword.wrn @@ -29,6 +29,8 @@ component TogglePassword { invalid: boolean = false helpText: string = "" validationMessage: string = "" + cornerHint: string = "" + cornerHref: string = "" class: string = "" } @@ -98,7 +100,12 @@ component TogglePassword { {/each}
    {:else} - +
    + + {#if cornerHint} + {#if cornerHref}{cornerHint}{:else}{cornerHint}{/if} + {/if} +