The central gotcha this skill exists to prevent: beforeLoad + redirect() protects the UI only — server functions and server routes are independently reachable API endpoints, so authorization MUST be enforced inside the server-function handler or its middleware, never assumed from a route guard.
Ships DIY server-primitive templates (portable, no vendor lock-in) for an _authed layout + authMiddleware, session/CSRF helpers, and OAuth authorization-code flow with PKCE. For a managed auth library, install it and follow its own current docs or the dedicated better-auth skill rather than assuming a Start adapter API.
Do NOT use this skill for generic route guards unrelated to auth (react-tanstack-router) or non-Start Node auth setups.
TanStack Start — Authentication
CRITICAL GOTCHA (READ BEFORE ANYTHING)
beforeLoad + redirect() protects the UI, NOT your data.
Server functions and server routes are API endpoints reachable independently of whichever route renders the calling component. A beforeLoad guard keeps a user off a screen, but the underlying createServerFn handler can still be called directly (crafted request, replayed RPC). Authorization MUST be enforced inside the server-function handler or its middleware — that is the security boundary. beforeLoad is route UX only.
Route beforeLoad guard → UX: keep users out of screens they can't use
Server-fn middleware → SECURITY: the real data/API boundary — enforce auth HERE
→ data-boundary.md is mandatory reading before writing any auth code.
Agent Workflow (MANDATORY)
Before ANY implementation, spawn in parallel:
- fuse-ai-pilot:explore-codebase — find
src/routes/_authed*,src/server/, existing session code - fuse-ai-pilot:research-expert — verify Start auth API via Context7
/websites/tanstack_start_framework_react - mcp__context7__query-docs — confirm
useSession,createMiddleware,getRequestsignatures
After implementation, run fuse-ai-pilot:sniper, then consider fuse-security:auth-audit.
Overview
| Concern | Primitive |
|---|---|
| Route UX | beforeLoad + redirect({ to: '/login' }) in an _authed layout route |
| Data authorization | authMiddleware on every private createServerFn (the real boundary) |
| Sessions | useSession<T>() (sealed cookie) OR manual __Host- cookie via getRequest/setResponseHeader |
| CSRF | createCsrfMiddleware() (auto for server fns) + Origin check for sibling subdomains |
Critical Rules
- Authorize in the handler — every server fn touching private data carries
authMiddleware; never rely onbeforeLoad. - Never a GET that mutates — mutations use POST/PUT/DELETE so
SameSite=Laxprotects them. - Read env/cookies per request —
process.env.SECRETinside the handler, NEVER at module scope (leaks to bundle; undefined on edge). - Rotate sessions on privilege change — revoke old + issue new on login/logout/password/role change.
- Defeat enumeration & timing — identical responses for unknown vs known accounts; constant-time password compare.
Auth Approaches (official)
The official authentication guide lists Clerk, WorkOS, Better Auth, and Auth.js as supported options, plus fully DIY. This skill ships the DIY server-primitive templates (portable, no vendor lock-in), which the official guide documents in depth. For a managed library (Auth.js, Better Auth, Clerk, WorkOS), install it and follow ITS current docs / the better-auth skill — do NOT assume a Start adapter API without verifying it, and note there is no first-party start-authjs example in the TanStack repo (the real DIY examples are start-basic-auth and start-supabase-basic).
Reference Guide
Concepts
| Topic | Reference | Load when |
|---|---|---|
| Data boundary | data-boundary.md | ALWAYS first — where auth is actually enforced |
| Route protection | route-protection.md | Building the _authed layout + RBAC redirects |
| Sessions & cookies | sessions-cookies.md | Issuing/reading sessions, cookie flags |
| Hardening | hardening.md | CSRF, rate limiting, OAuth state/PKCE, timing |
Templates
| Template | When to Use |
|---|---|
| authed-middleware.md | _authed layout + authMiddleware + protected server fn |
| session-and-csrf.md | Cookie session helpers + global CSRF/origin middleware + login |
| oauth-pkce.md | OAuth authorization-code flow with state + PKCE |
Best Practices
DO
- Centralize session lookup in
authMiddlewareso every handler gets a typed session - Use
__Host-prefixed,HttpOnly,Secure,SameSite=Laxcookies - Verify
Originon non-GET requests against your app origin
DON'T
- Treat
beforeLoadas the security boundary - Read
process.envat module top level - Vary response/status/timing between existing and non-existing accounts