QyzarQyzar/

Docs

Dashboard

Start

  • Overview
  • Quick start

SDKs

  • Browser SDK
  • Server helper

Guides

  • Client tokens
  • Verify sessions
  • Rate limiting
  • Uptime & status

API

  • API basics
  • HTTP routes

~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 id
  • qyzar_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.

Next.js — server action or route (QyzarServer)
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",
  });
}
Next.js — layout passes token to the SDK
// 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:

Client component — after verify
"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:

Client — cookies only
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.

Next.js App Router — cookies() on mint
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
  );
}
Next.js — API route remint from POST body
// 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.

Server — explicit session fields
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)

POST /projects/client/token
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_SECRET in the browser or NEXT_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_token appear or change.
  • Do not call setConnectSessionCookies yourself unless you implement verify outside the SDK — normal apps rely on ConnectVerifier after connect/verify.