#Generating Sessions with better auth component

12 messages · Page 1 of 1 (latest)

fleet sorrel
#

With the better auth component, what's the best way to mint a session using betterAuths server APIs and then pass that to a react or tanstack start front-end?

pallid quailBOT
#

Hi @fleet sorrel. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:

#

There isn’t anything in the knowledge sources that describes manually minting a Better Auth session via auth.api and then handing that session to a React or TanStack Start frontend. All of the documented flows assume that sessions are created via the Better Auth client (authClient.signIn.*) in the browser, and then reused on the server via cookies. I’ll outline what is documented and how it fits together.

1. How sessions are normally created and propagated

On the client (React or TanStack Start), you use the Better Auth client:

// React SPA example
import { createAuthClient } from "better-auth/react";
import {
  convexClient,
  crossDomainClient,
} from "@convex-dev/better-auth/client/plugins";

export const authClient = createAuthClient({
  baseURL: import.meta.env.VITE_CONVEX_SITE_URL,
  plugins: [convexClient(), crossDomainClient()],
});

You then sign in with authClient.signIn.*, which talks to the Convex Better Auth routes and sets cookies in the browser. [React guide]

Those cookies are what the Convex side uses to identify the session.

2. Using the session from Convex server code

On the Convex side, you don’t mint sessions; you consume them. The component client gives you helpers:

import { authComponent } from "./auth";
import { createAuth } from "./auth";

export const updateUserPassword = mutation({
  args: { currentPassword: v.string(), newPassword: v.string() },
  handler: async (ctx, args) => {
    const { auth, headers } = await authComponent.getAuth(createAuth, ctx);
    await auth.api.changePassword({
      body: {
        currentPassword: args.currentPassword,
        newPassword: args.newPassword,
      },
      headers,
    });
  },
});
#

authComponent.getAuth(createAuth, ctx) reads the Better Auth cookies from the request, validates the session, and gives you both auth and the headers you must pass to auth.api.* methods. [Basic usage; Component client]

3. Making the session available to React / TanStack Start

For SSR frameworks, the documented pattern is:

  1. Use a server utility to read the session token from cookies.
  2. Pass that token into Convex / Better Auth providers so the client starts authenticated.

TanStack Start

You configure server utilities:

// src/lib/auth-server.ts
import { convexBetterAuthReactStart } from '@convex-dev/better-auth/react-start'

export const {
  handler,
  getToken,
  fetchAuthQuery,
  fetchAuthMutation,
  fetchAuthAction,
} = convexBetterAuthReactStart({
  convexUrl: process.env.VITE_CONVEX_URL!,
  convexSiteUrl: process.env.VITE_CONVEX_SITE_URL!,
})

Then in the root route:

// src/routes/__root.tsx
const getAuth = createServerFn({ method: 'GET' }).handler(async () => {
  return await getToken();
});

export const Route = createRootRouteWithContext<...>()({
  beforeLoad: async (ctx) => {
    const token = await getAuth();

    if (token) {
      ctx.context.convexQueryClient.serverHttpClient?.setAuth(token);
    }

    return {
      isAuthenticated: !!token,
      token,
    };
  },
  component: RootComponent,
});

function RootComponent() {
  const context = useRouteContext({ from: Route.id });
  return (
    <ConvexBetterAuthProvider
      client={context.convexQueryClient.convexClient}
      authClient={authClient}
      initialToken={context.token}
    >
      <RootDocument>
        <Outlet />
      </RootDocument>
    </ConvexBetterAuthProvider>
  );
}
#

Here, getToken() reads the existing Better Auth session from cookies and returns a Convex auth token; that token is then passed to ConvexBetterAuthProvider.initialToken and to the ConvexQueryClient for SSR. [TanStack install; Set up root route; Pass initial token]

Next.js

The same idea applies: use convexBetterAuthNextJs to get getToken, then pass that token into your Convex provider as initialToken. [SSR improvements; Pass initial token]

4. What’s not documented

The docs and Discord thread you provided do not show:

  • A supported way to call a Better Auth server API (e.g. auth.api.*) to create a session and then manually inject that into the frontend.
  • Any example of minting a session token on Convex and then handing it to the browser outside of the normal authClient.signIn.* flow.

They explicitly note that sign up / sign in / sign out must be done from the client because Convex functions can’t set cookies. [Basic usage]

So, based on the available docs:

  • Sessions are minted via Better Auth’s HTTP endpoints, typically through the client SDK in the browser.
  • Convex and your SSR framework then consume that session via cookies and getToken, and you pass that token into ConvexBetterAuthProvider / ConvexQueryClient.
#

If you need a truly server-initiated login flow (e.g. minting a session entirely on the server and then handing it to the client), the knowledge sources don’t describe a supported pattern for that.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:

#

@fleet sorrel Kapa.ai is still learning and improving, please let me know how I did by reacting below.

fleet sorrel
#

@pallid quail the backend component for betterAuth contains all the methods including the auth.api. I want users to sign in via a link with a query param token which is then verified on the front-end and then exchanged for a session in the backend. How can i get the cookie created by the backend to the front-end

pallid quailBOT
#

Let me check the knowledge base for you again. One moment! :books:

#

The knowledge sources don’t describe a flow where you:

  1. Hit a Convex/Better Auth backend endpoint with a token from a link,
  2. Call auth.api.* there to create a session,
  3. And have that backend endpoint set cookies that the browser then uses.

In fact, they explicitly say the opposite: sign up / sign in / sign out must be done from the client, because Convex functions don’t return HTTP responses or set cookies. [Basic usage]

“Because Convex functions run over websockets and don't return HTTP responses or set cookies, signing up/in/out must be done from the client via authClient.signIn.* methods.” [Basic usage]

So there is no documented way to “get the cookie created by the backend to the front‑end” via a Convex function, because Convex never sets those cookies in the first place.

What is documented and supported is:

  1. All session cookie handling is done by Better Auth’s HTTP routes (the /api/auth/... endpoints wired up via handler / nextJsHandler / reactStartHandler). Those routes run in your framework server (Next.js, TanStack Start), not in Convex. [TanStack install; TanStack route handler]

  2. Convex only consumes the session via cookies:

    • On the Convex side you use authComponent.getAuth(createAuth, ctx) to get auth + headers for auth.api.*, based on the cookies already present. [Component client; Basic usage]
#
  1. Your frontend gets an auth token (not the cookie) from the framework server, using helpers like getToken:
    • TanStack Start: convexBetterAuthReactStart gives you getToken, fetchAuthQuery, etc. [TanStack server utils]
    • You call getToken() in a server function, then:
      • Set serverHttpClient?.setAuth(token) for SSR, and
      • Pass initialToken={token} into ConvexBetterAuthProvider so the client starts authenticated. [Set up root route; Pass initial token]

Given that, the documented pattern for your “magic link with query param token” flow would need to be:

  • Verify and exchange the link token in your framework’s HTTP layer, not in Convex:

    • Implement a route under the Better Auth base path (or a custom route that calls Better Auth’s HTTP API) that:
      • Verifies the query param token,
      • Calls the appropriate Better Auth server method to create a session,
      • Lets Better Auth set the session cookies in the HTTP response.
  • Let the browser follow that HTTP redirect/response so the cookies are stored.

#
  • Then use the existing Better Auth + Convex integration:
    • On SSR, call getToken() (from convexBetterAuthReactStart / convexBetterAuthNextJs) to read the session from cookies and get a Convex JWT.
    • Pass that token into:
      • ctx.context.convexQueryClient.serverHttpClient?.setAuth(token) in beforeLoad (TanStack Start), and
      • initialToken on ConvexBetterAuthProvider. [Set up root route; [SSR improvements]

What you cannot do, per the docs:

  • Call auth.api.* inside a Convex function to create a session and expect cookies to be set for the browser; Convex has no HTTP response to attach cookies to. [Basic usage]

So, in short:

  • Session cookies must be created in your framework’s HTTP routes (Better Auth handler), not in Convex.
  • The frontend “gets” those cookies simply by following that HTTP response; Convex then reads them via getToken() / authComponent.getAuth.
  • There is no documented mechanism to mint a session purely in Convex and then push its cookies to the browser.

Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: