| name | tanstack-start-auth |
| description | Better Auth on TanStack Start: betterAuth() config, plugins, adapters, sessions, and FMC route protection (layout beforeLoad for route UX, endpointAuth/guards for data boundaries, headers, tanstackStartCookies plugin, OAuth sign-in). Use for auth.ts, Better Auth, sign-in flows, session helpers, or secured routes/APIs. |
| disable-model-invocation | true |
TanStack Start + Better Auth
Single skill for library configuration and Start-specific wiring in FMC apps (TILDA, Trassenscout migration, etc.).
When to apply
- Creating or changing
betterAuth() / createAuthClient() config
- Mounting
/api/auth/*, tanstackStartCookies() plugin, OAuth sign-in routes
beforeLoad gates, endpointAuth, session in server functions or API handlers
- Public vs protected server functions (
public*.functions.ts naming)
- API key + session dual auth on automation endpoints
References (read as needed)
Live docs: better-auth.com/docs · llms.txt
Related skills
tanstack-start-conventions — layout, .server.ts / .functions.ts, boundaries, SSR
playwright-skill — E2E stubbed auth (TILDA)
Non-negotiable rules
- Session helpers always receive
headers: Headers from the current request.
- Route
beforeLoad does not call DB/session directly — server functions + getRequestHeaders().
- Use official
tanstackStartCookies() as the last plugin; verify no client-bundle leak after better-auth version changes (auth.md).
/api/auth/$ and other handler-only API routes: ssr: false (selective-ssr.md).
- API routes: auth inside each handler (no route-level
beforeLoad).
- Admin/role checks with cookie cache:
getSession({ query: { disableCookieCache: true } }).
- Compare API secrets with timing-safe equality.
- Two layers:
beforeLoad = route UX only; endpointAuth / guards = data boundary. Never rely on beforeLoad alone.
- No auth middleware for route guarding — use layout
beforeLoad. Global auth middleware breaks public RPCs (public*.functions.ts, getSessionForRouteFn) and is not the FMC auth-route pattern.
Quick patterns
await routeSessionFn({ data: location })
const session = await endpointAuth.session(request.headers)
const headers = getRequestHeaders()
await endpointAuth.admin(headers)
Setup snapshot
betterAuth() in auth.server.ts (Prisma/Drizzle adapter, plugins)
createAuthClient() with matching client plugins
/api/auth/$ → auth.handler(request) (cookies via tanstackStartCookies() plugin)
session.server.ts helpers taking Headers
- CLI migrate/generate after plugin changes — details in better-auth-config.md