Sessions
createAuthSession(auth) binds two helpers to your auth instance:
export const { getSession, requireSession } = createAuthSession(auth);getSession()→ the current session, ornullfor anonymous users.requireSession()→ the session, or a thrown redirect when there is none.
One choke point, two ways to authenticate
There are two ways to be authenticated, and they are resolved in one place so the rest of the app never has to care which was used:
- the Better Auth cookie (browsers), or
- an
Authorization: Bearer sk_live_…API token (scripts, CI, integrations).
Deactivated users (an admin set deactivatedAt) are collapsed to anonymous at
this same choke point — so deactivation locks out existing cookies and tokens
immediately, with no extra check scattered through the app.
Because the token path returns a session-shaped object, callers like
requireSession (and an app's requireSiteAdmin) are byte-for-byte identical
whether the caller arrived with a cookie or a token.
The resolved shape
Both paths resolve to the same type. The only tell is .session: a cookie
session carries the Better Auth session row; a token "session" sets it to null.
async function () {
const = await ();
return ..;
}Contrast the two return types: getSession() is Session | null (you must
handle anonymous), while requireSession() is a non-null Session.
Using them in server functions
getSession — branch on anonymous yourself:
import { createServerFn } from "@tanstack/react-start";
import { getSession } from "#/lib/auth";
export const loadDashboard = createServerFn().handler(async () => {
const session = await getSession();
if (!session) return { signedIn: false as const };
return { signedIn: true as const, email: session.user.email };
});requireSession — let it throw. The thrown Response is a 302 to /login,
which TanStack Start turns into a redirect:
import { createServerFn } from "@tanstack/react-start";
import { requireSession } from "#/lib/auth";
export const loadSettings = createServerFn().handler(async () => {
const session = await requireSession(); // throws → redirect to /login
return { email: session.user.email };
});