SSR Overview

The server() helper from @robelest/convex-auth/server gives your SSR framework a single entry point for OAuth code exchange, token refresh, and httpOnly cookie management. It works with any framework that gives you access to the incoming Request object.

Basic usage

import { server } from "@robelest/convex-auth/server";

const auth = server({ url: process.env.CONVEX_URL! });

// In your server handler / middleware:
const result = await auth.refresh(request);

if (result.redirect) {
  return result.response;
}

const { cookies, token } = result;

auth.refresh(request) reads the auth cookies from the incoming request, exchanges or refreshes tokens with your Convex backend, and returns either a ready-made redirect Response or the cookies and token needed to continue the current SSR response.

Return shape

auth.refresh(request) returns a discriminated union:

Redirect branch

{ redirect: true, response: Response }

When OAuth code exchange occurs, Convex Auth builds the redirect Response including any Set-Cookie headers for you. Return response directly from your framework handler.

Non-redirect branch

{ redirect: false, cookies: AuthCookie[], token: string | null }

When no redirect is needed, apply cookies to the outgoing response and use token for SSR hydration or server-side Convex calls.

Each AuthCookie object contains name, value, and standard cookie options (httpOnly, secure, sameSite, path, maxAge). How you apply them depends on your framework — see the framework-specific guides.

Proxying client requests

For client-side sign-in and sign-out flows, use auth.proxy() to forward POST requests to your Convex backend:

const response = await auth.proxy(request);

proxy() handles:

  • Sign-in — forwards credentials to Convex, returns Set-Cookie headers with the new session tokens.
  • Sign-out — clears the session on the backend and returns cookie-clearing headers.

Mount this behind a /api/auth route (or similar) and point your client-side auth calls to that endpoint.

Options

acceptedIssuers

By default, server() only accepts tokens issued by your Convex deployment. If you need to accept tokens from additional issuers (e.g. a custom OIDC provider), pass them in the acceptedIssuers array:

const auth = server({
  url: process.env.CONVEX_URL!,
  acceptedIssuers: ["https://auth.example.com"],
});

Client-side auth

The client() function from @robelest/convex-auth/browser creates the client-side auth state manager. It works with any Convex client transport.

Use @robelest/convex-auth/browser for browser apps and SSR frameworks because it wires in browser defaults like URL handling, local storage, sync, and passkey support. Use @robelest/convex-auth/client only when you need the framework-agnostic client in a non-browser runtime or want to provide those runtime pieces yourself.

import { client as createAuthClient } from "@robelest/convex-auth/browser";

const auth = createAuthClient({
  convex: convexClient,
  proxyPath: "/api/auth",
  tokenSeed: serverToken,
  location: () => currentUrl, // SSR-safe URL source
});

location option

Pass a URL source so the client can safely read query parameters during SSR (where window is not available). Each framework provides this differently:

  • SvelteKit: location: () => page.url (from $app/state)
  • Next.js: pass from server props or useSearchParams()
  • TanStack Start: pass from useServerFn() or loader data
  • SPA: omit (defaults to window.location with SSR guard)

auth.param(name)

SSR-safe URL parameter reader. Uses the location option, falls back to window.location when available:

const workspaceId = auth.param("workspace");

auth.invite

The client automatically detects invite tokens from ?invite= URL parameters and persists them across OAuth redirects. After authentication, the app can consume the invite:

if (auth.invite) {
  const { token } = await auth.invite.accept();
  // Use the token to call your accept mutation
  await client.mutation(api.acceptInvite, { token });
}

The client handles:

  • Reading ?invite= and ?email= from the URL
  • Persisting the token to storage before signIn() (survives OAuth redirects)
  • Recovering the token from storage after redirect
  • Cleaning up URL parameters after accept()

Next steps

See the framework-specific guides for full integration examples: