~3 min read
Client tokens
Your server creates a short-lived JWT so the browser can use Qyzar without ever seeing the project secret.
Where the session lives
After a successful connect/verify, @tnuser/qyzar-client writes two cookies on your site origin (via ConnectVerifier or Qyzar):
qyzar_session_id— connect session document idqyzar_session_token— session JWT
Defaults: Path=/, SameSite=Lax, 15-minute sliding Max-Age, and Secure on HTTPS. Cookie names can match your API env (QYZAR_SESSION_ID_COOKIE / QYZAR_SESSION_TOKEN_COOKIE) when using custom names on the server.
First visit (no session yet)
Mint a token with only the project id. Pass it into the React SDK.
import { QyzarServer } from "@tnuser/qyzar-server";
const qyzar = new QyzarServer({
apiBaseUrl: process.env.QYZAR_API_URL!,
resolveProjectSecret: async () => process.env.QYZAR_PROJECT_SECRET!,
});
export async function getClientTokenForPage() {
const projectId = process.env.NEXT_PUBLIC_QYZAR_PROJECT_ID!;
return qyzar.createClientToken(projectId, {
identifier: "anonymous",
});
}// app/layout.tsx (server component)
import { Qyzar } from "@tnuser/qyzar-client";
import { getClientTokenForPage } from "./qyzar-token";
export default async function RootLayout({ children }) {
const clientToken = await getClientTokenForPage();
return (
<html>
<body>
<Qyzar
projectId={process.env.NEXT_PUBLIC_QYZAR_PROJECT_ID!}
clientToken={clientToken}
>
{children}
</Qyzar>
</body>
</html>
);
}Read session from the browser (client)
Use the SDK helpers (recommended). They read the same cookies the verifier sets after success:
"use client";
import {
getConnectSessionId,
getConnectSessionToken,
DEFAULT_CONNECT_SESSION_ID_COOKIE,
} from "@tnuser/qyzar-client";
export function useHasQyzarSession(): boolean {
return Boolean(getConnectSessionId());
}
export async function remintClientTokenOnServer() {
const sessionId = getConnectSessionId();
const sessionToken = getConnectSessionToken();
if (!sessionId || !sessionToken) return null;
const res = await fetch("/api/qyzar/remint-client-token", {
method: "POST",
credentials: "include", // also sends Cookie header
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ sessionId, sessionToken }),
});
if (!res.ok) return null;
const { clientToken } = await res.json();
return clientToken as string;
}
// Optional: raw document.cookie (same names as defaults)
function readRawSessionId(): string | null {
const prefix = `${DEFAULT_CONNECT_SESSION_ID_COOKIE}=`;
for (const part of document.cookie.split(";")) {
const s = part.trim();
if (s.startsWith(prefix)) return decodeURIComponent(s.slice(prefix.length));
}
return null;
}Or call your own route with cookies only — no JSON body required if the route reads Cookie on the server:
const res = await fetch("/api/qyzar/client-token", {
method: "GET",
credentials: "include",
cache: "no-store",
});
const { clientToken } = await res.json();Read session on the server (SSR / API)
Forward the visitor's Cookie header when minting so Qyzar can bind the JWT to the connect session.
import { cookies } from "next/headers";
import { QyzarServer } from "@tnuser/qyzar-server";
const qyzar = new QyzarServer({ /* … */ });
function parseConnectSession(cookieHeader: string) {
let sessionId: string | undefined;
let sessionToken: string | undefined;
for (const part of cookieHeader.split(";")) {
const [rawName, ...rest] = part.trim().split("=");
const name = rawName?.trim();
const value = decodeURIComponent(rest.join("=").trim());
if (name === "qyzar_session_id") sessionId = value;
if (name === "qyzar_session_token") sessionToken = value;
}
return { sessionId, sessionToken };
}
export async function getClientTokenForRequest() {
const projectId = process.env.NEXT_PUBLIC_QYZAR_PROJECT_ID!;
const jar = await cookies();
const cookieHeader = jar
.getAll()
.map((c) => `${c.name}=${c.value}`)
.join("; ");
const { sessionId, sessionToken } = parseConnectSession(cookieHeader);
return qyzar.createClientToken(
projectId,
{ identifier: "site-visitor" },
sessionId && sessionToken ? { sessionId, sessionToken } : undefined
);
}// app/api/qyzar/remint-client-token/route.ts
import { NextResponse } from "next/server";
import { qyzar } from "@/lib/qyzar";
export async function POST(req: Request) {
const projectId = process.env.NEXT_PUBLIC_QYZAR_PROJECT_ID!;
const body = await req.json();
const sessionId = String(body.sessionId ?? "").trim();
const sessionToken = String(body.sessionToken ?? "").trim();
if (!sessionId || !sessionToken) {
return NextResponse.json({ error: "Missing session" }, { status: 400 });
}
const clientToken = await qyzar.createClientToken(
projectId,
{ identifier: "site-visitor" },
{ sessionId, sessionToken }
);
return NextResponse.json({ clientToken });
}After the visitor verifies
Flow: SDK sets cookies → your app reads them (client or server) → remint clientToken → pass the new token to Qyzar / QyzarSiteLayout.
const sessionId = body.sessionId; // from browser helpers or cookies()
const sessionToken = body.sessionToken;
const clientToken = await qyzar.createClientToken(
projectId,
{ identifier: user.id, email: user.email },
{ sessionId, sessionToken }
);Raw HTTP (no server package)
const res = await fetch("https://apisecure.qyzar.eu/projects/client/token", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.QYZAR_PROJECT_SECRET}`,
"Content-Type": "application/json",
Accept: "application/json",
// optional: forward browser cookies instead of JSON session fields
Cookie: cookieHeaderFromRequest,
},
body: JSON.stringify({
projectId: "your-project-uuid",
identifier: "user_123",
email: "[email protected]",
// optional, after verify (or omit when Cookie header is set):
sessionId: "…",
sessionToken: "…",
}),
});
const { clientToken } = (await res.json()).data;Rules
- Never put
QYZAR_PROJECT_SECRETin the browser orNEXT_PUBLIC_*. - A client token is not enough to trust checkout or account APIs — use session verification for that.
- Remint when
qyzar_session_id/qyzar_session_tokenappear or change. - Do not call
setConnectSessionCookiesyourself unless you implement verify outside the SDK — normal apps rely onConnectVerifierafterconnect/verify.
