Problem
The frontend authentication flow cannot complete. Three layers fail independently:
-
AuthProvider calls a route handler that does not exist in the live router. app/components/auth-provider.tsx boots with fetch('/api/auth/refresh', { method: 'POST' }). The only route handler under the live src/app router is app/src/app/api/health/route.ts — there is no /api/auth/refresh there. The refresh call 404s, setState('unauthenticated') runs, and the user is redirected.
-
The middleware redirects to a page that does not exist. app/src/middleware.ts sends unauthenticated /dashboard/* visitors to /auth/login. The login page exists at app/auth/login/page.tsx — outside the compiled router (see the app-router resolution issue, this issue's dependency) — so /auth/login is a 404 page.
-
The auth route handlers that do exist are dead code and hardcode a local URL. app/api/auth/login/route.ts, app/api/auth/refresh/route.ts, and app/api/auth/logout/route.ts live at the project root, outside the router, and all three call http://localhost:3001/... directly. In any deployment where the API is not on the developer's localhost they cannot work, even if they were reachable.
Consequence: a fresh visitor to the app is bounced between a nonexistent refresh endpoint and a nonexistent login page. Registration (app/auth/register/page.tsx) exists as a page but its form posts to the same dead route layer. The product cannot be used through the web UI at all.
Root cause
// app/components/auth-provider.tsx
const res = await fetch('/api/auth/refresh', { method: 'POST' }) // ← no such route in src/app
// app/src/middleware.ts
if (isDashboardRoute && !token) {
return NextResponse.redirect(new URL('/auth/login', request.url)) // ← no such page
}
Why this is architecturally hard
- The auth route handlers are the bridge between the browser (httpOnly cookie) and the API (JWT). The current design proxies
POST /auth/refresh from a Next route handler so the cookie never leaves the same-site context. Moving these handlers into the live router is the first step, but the proxy must also stop hardcoding http://localhost:3001 — the API base URL must come from configuration (NEXT_PUBLIC_API_URL is already read in app/lib/api/*.ts; the route handlers ignore it).
- The API's refresh contract is cookie-only (
api/src/auth/auth.controller.ts reads req.cookies.refresh_token). The Next proxy exists precisely because of that. Fixing the flow on the app side cannot change the API contract, so the proxy must forward the cookie (it already tries to) and must do so from a reachable location.
- The login/register pages (
app/auth/login/page.tsx, app/auth/register/page.tsx) live in the pre-src layout; their fate is decided by the layout consolidation. This issue should not re-architect the layout — it should land after the router resolution issue and move/rewire the auth pages and routes into whichever layout becomes canonical.
Downstream impact
Depends on the app-router resolution issue (the empty-app/app router fix): until a layout is canonical, no page or route handler in this issue's scope is reachable. Beyond that dependency this is repo-local; the API contract is unchanged.
Acceptance criteria
Flow
Config
Tests
Documentation
Out of scope
Attaching the access token to dashboard API calls (separate issue), token refresh UI/UX, and any change to the API's cookie-based refresh contract.
Getting started
Real files in scope: app/api/auth/login/route.ts, app/api/auth/refresh/route.ts, app/api/auth/logout/route.ts (move into the canonical router location), app/auth/login/page.tsx, app/auth/register/page.tsx, app/components/auth-provider.tsx, app/src/middleware.ts, app/src/app/api/health/route.ts (pattern for a live route handler).
Verify with:
cd app && npm run typecheck && npm test && npm run build
Good first files to read: app/components/auth-provider.tsx, app/src/middleware.ts, app/api/auth/refresh/route.ts.
Problem
The frontend authentication flow cannot complete. Three layers fail independently:
AuthProvidercalls a route handler that does not exist in the live router.app/components/auth-provider.tsxboots withfetch('/api/auth/refresh', { method: 'POST' }). The only route handler under the livesrc/approuter isapp/src/app/api/health/route.ts— there is no/api/auth/refreshthere. The refresh call 404s,setState('unauthenticated')runs, and the user is redirected.The middleware redirects to a page that does not exist.
app/src/middleware.tssends unauthenticated/dashboard/*visitors to/auth/login. The login page exists atapp/auth/login/page.tsx— outside the compiled router (see the app-router resolution issue, this issue's dependency) — so/auth/loginis a 404 page.The auth route handlers that do exist are dead code and hardcode a local URL.
app/api/auth/login/route.ts,app/api/auth/refresh/route.ts, andapp/api/auth/logout/route.tslive at the project root, outside the router, and all three callhttp://localhost:3001/...directly. In any deployment where the API is not on the developer's localhost they cannot work, even if they were reachable.Consequence: a fresh visitor to the app is bounced between a nonexistent refresh endpoint and a nonexistent login page. Registration (
app/auth/register/page.tsx) exists as a page but its form posts to the same dead route layer. The product cannot be used through the web UI at all.Root cause
Why this is architecturally hard
POST /auth/refreshfrom a Next route handler so the cookie never leaves the same-site context. Moving these handlers into the live router is the first step, but the proxy must also stop hardcodinghttp://localhost:3001— the API base URL must come from configuration (NEXT_PUBLIC_API_URLis already read inapp/lib/api/*.ts; the route handlers ignore it).api/src/auth/auth.controller.tsreadsreq.cookies.refresh_token). The Next proxy exists precisely because of that. Fixing the flow on the app side cannot change the API contract, so the proxy must forward the cookie (it already tries to) and must do so from a reachable location.app/auth/login/page.tsx,app/auth/register/page.tsx) live in the pre-srclayout; their fate is decided by the layout consolidation. This issue should not re-architect the layout — it should land after the router resolution issue and move/rewire the auth pages and routes into whichever layout becomes canonical.Downstream impact
Depends on the app-router resolution issue (the empty-
app/approuter fix): until a layout is canonical, no page or route handler in this issue's scope is reachable. Beyond that dependency this is repo-local; the API contract is unchanged.Acceptance criteria
Flow
POST /api/auth/refreshexists as a reachable route handler and returns 200 with{ user, accessToken }when a valid refresh cookie is present./auth/loginand/auth/registerrender as pages (not 404) and submit through the reachable proxy routes./dashboard/streamsis redirected to a working login page and, after login, lands back on the dashboard withAuthProviderin theauthenticatedstate.AuthProviderrestores the session without redirecting to login.Config
http://localhost:3001; the API base URL is resolved from config (NEXT_PUBLIC_API_URLor equivalent) consistently withapp/lib/api/streams.ts.Tests
AuthProvider(app/components/auth-provider.test.tsx) cover the 200 and 401/404 refresh paths./dashboard/*request and pass-through for authenticated ones.Documentation
Out of scope
Attaching the access token to dashboard API calls (separate issue), token refresh UI/UX, and any change to the API's cookie-based refresh contract.
Getting started
Real files in scope:
app/api/auth/login/route.ts,app/api/auth/refresh/route.ts,app/api/auth/logout/route.ts(move into the canonical router location),app/auth/login/page.tsx,app/auth/register/page.tsx,app/components/auth-provider.tsx,app/src/middleware.ts,app/src/app/api/health/route.ts(pattern for a live route handler).Verify with:
Good first files to read:
app/components/auth-provider.tsx,app/src/middleware.ts,app/api/auth/refresh/route.ts.