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-Cookieheaders 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.locationwith 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: