Skip to content

dogfood: saas dashboard shows literal ${user...} and login/signup don't reach /dashboard #831

Description

@vivek7405

Problem

Two auth-flow bugs in the saas scaffold template, both found by building and running a generated saas app.

1. The dashboard renders a literal ${user?.name || user?.email} instead of the user's name. The generated app/dashboard/page.ts fetches const user = await currentUser() (so user IS in scope), but the greeting is written as an over-escaped literal, so a signed-in user sees the raw text "Welcome, ${user?.name || user?.email}!". Same over-escaping in app/dashboard/settings/page.ts for the email and name.

2. Login and signup do not land on /dashboard. After a successful credential login (the form posts to /api/auth/signin/credentials), the user is redirected to / (the createAuth default), not the dashboard. After signup, the action redirects to /login, and even then login does not reach the dashboard. So a signed-in user never automatically arrives at the protected page.

Design / approach

  • Bug 1 is a pure escaping mistake. The saas page files are assembled from arrays of DOUBLE-QUOTED strings joined with \n (not template literals), so ${...} is already emitted literally into the generated file. The author additionally escaped it to ${\${user?.name || user?.email}`}, which evaluates (in the generated html``template) to the literal text. Fix: write the interpolation plainly,${user?.name || user?.email}`.
  • Bug 2 (login): createAuth's signin honors a redirectTo field from the POSTed form data (packages/server/src/auth.js:293, const redirectTo = opts.redirectTo || (data && data.redirectTo) || '/'). Add a hidden <input type="hidden" name="redirectTo" value="/dashboard"> to the login form.
  • Bug 2 (signup): auto-login on success. A page action may return a raw Response (packages/server/src/page-action.js:226 passes it through), and signIn('credentials', { email, password }, { redirectTo: '/dashboard' }) returns a 302 with the session set-cookie. So on a successful signup, return signIn('credentials', { email, password }, { redirectTo: '/dashboard' }) instead of { success: true, redirect: '/login' }. (The signup page is elided, so importing signIn from #lib/auth.server.ts does not ship to the browser; verify webjs check stays clean.)

Implementation notes (for the implementing agent)

  • Where to edit: packages/cli/lib/create.js's saas generator, packages/cli/lib/saas-template.js:
    • Dashboard greeting: line ~439 (app/dashboard/page.ts); settings: lines ~471, ~473 (app/dashboard/settings/page.ts). Remove the \${...}`wrapper, leave${...}`.
    • Login form: line ~310 area (app/login/page.ts form) add the hidden redirectTo input.
    • Signup action: line ~359-362 (app/signup/page.ts action) import signIn from #lib/auth.server.ts and return its Response on success.
  • Landmines: the file is built from double-quoted string arrays, so ${...} must NOT be escaped (that is the whole bug); a single quote inside is fine. signIn needs the credentials to match what Credentials.authorize reads (email, password). Confirm the signup page stays elided so the #lib/auth.server.ts import does not trip no-server-import-in-browser-module.
  • Invariants: keep the no-JS write path working (the forms must still submit without JS); the login redirectTo must be a same-site local path.
  • Tests + docs: the generated test/auth/auth.test.ts asserts signup is a 303 to /login (line ~255-258) and that the dashboard renders "Dashboard"; update those to the new flow (signup success is now a 302 to /dashboard with a session cookie, and assert the dashboard greets the real user name, which also counterfactually catches the literal-interpolation regression). Regenerate a saas app and verify the full signup -> dashboard and login -> dashboard flows in a browser.

Acceptance criteria

  • A signed-in dashboard shows the real user name/email, not the literal ${...} text (settings too).
  • Successful login lands on /dashboard.
  • Successful signup auto-logs-in and lands on /dashboard.
  • The no-JS <form> path still works for both login and signup.
  • webjs check stays clean (signup page's signIn import does not ship).
  • The generated test/auth/auth.test.ts is updated to the new flow and passes, with the dashboard-name assertion counterfactually catching the literal-interpolation bug.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Fields

No fields configured for issues without a type.

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions