SDK (B2B2C)Auth & session tokens

Auth & session tokens

Every partner integration ends up calling kernel-api with a short-lived cbv1.* session token. There are two ways to get that token into the browser, and most partners only need one of them:

FlowUse whenWhere the partner’s identity livesRefresh
Browser flow (keyId)Pure-frontend deploys: GitHub Pages, S3+CloudFront, Vercel static, Netlify, partner storefronts that don’t have a backend.The public half of the partner key (<keyId>) is in the bundle. The secret half stays out of the browser entirely.The SDK auto-mints + auto-refreshes.
Server flow (sessionToken)Partners that already have a backend they trust to hold secrets.The full cbsdk_<keyId>_<secret> is on the partner’s server.The partner’s server mints, the partner manages refresh.

The two flows hit the same POST /api/v1/sdk/session-tokens endpoint and produce the same cbv1.* token. The only difference is how the caller proves it may mint.

Provisioning a partner key

In hub, open Settings → SDK API keys → Generate key:

  • Label — human-readable, e.g. “Acme storefront — prod”.
  • Allowed origins — exact match list. No wildcards. Add every origin you plan to embed from, including http://localhost:3007 for local dev. The browser-sent Origin header is checked against this list both at mint time and against the cbv1 origin claim on every kernel-api request.
  • Allowed projects — slugs from your CADbuildr project list. Tokens can only be minted for projects in this list.
  • Default / max TTL — defaults to 30min / 2h.
  • Per-token render budget — optional integer cap on renders served per jti. Enforced via Redis. Null = unlimited.

The plaintext key is shown once at create time as cbsdk_<keyId>_<secret>. The two halves serve different purposes:

  • <keyId> (e.g. 7ef7b4133ef1e6c2) — publishable. Safe to ship in browser bundles; it identifies the partner but cannot mint a token by itself. It’s the half the browser flow uses.
  • _<secret>server-only. Combined with <keyId> proves the caller IS the partner. Used by the server flow.

Browser flow (keyId)

Best for purely static deployments. The SDK contacts hub on mount, mints a token, refreshes it before expiry.

import { CadbuildrProvider, CadbuildrViewer } from "@cadbuildr/sdk-react";
 
export function App() {
  return (
    <CadbuildrProvider
      keyId="7ef7b4133ef1e6c2"   // publishable half — safe in the bundle
      projectKey="lego"
    >
      <CadbuildrViewer dag={dag} />
    </CadbuildrProvider>
  );
}

Under the hood the SDK fires:

POST https://hub.cadbuildr.com/api/v1/sdk/session-tokens
Origin: https://store.acme.com          ← attached by the browser
Content-Type: application/json
 
{ "keyId": "7ef7b4133ef1e6c2", "projectId": "lego" }

Hub looks up the SDK key by keyId, verifies the Origin is in the key’s allowed_origins, and mints a cbv1.* scoped to that origin. The request has no Authorization header — origin allowlisting is the only proof of identity the browser can offer, and it’s enough because:

  • The browser auto-attaches Origin on every cross-origin fetch and JS can’t forge it.
  • The allowlist is per-key; another site embedding your keyId would fail the Origin check unless its origin is also on your allowlist.
  • Per-token TTL + per-jti budget + per-keyId rate limits keep scrape attempts cheap.

The <CadbuildrProvider> exposes the current mint state via useCadbuildrContext().tokenStatus:

type SessionTokenStatus =
  | { state: "loading" }            // first mount, or mid-refresh
  | { state: "ready"; token: string; expiresAt: number }
  | { state: "error"; error: string }   // hub unreachable / 403 / etc.
  | { state: "provided"; token: string }  // partner passed sessionToken instead

Use it to render a loading skeleton or surface a helpful error to the end-user. Call refreshSessionToken() to force a re-mint (e.g. after a 401 token_expired from kernel-api in your own fetch path).

Configuration

PropDefaultNotes
keyIdrequired for browser flowThe <keyId> half of your cbsdk_*.
projectKeyoptionalSent as projectId on mint; must be in the key’s allowed_projects.
hubBaseUrlhttps://hub.cadbuildr.comOverride to point at a staging hub if CADbuildr has provided one.
baseUrlhttps://kernel-api.cadbuildr.comkernel-api endpoint the cbv1 is sent to.

Server flow (sessionToken)

Best when you already have a backend you want to keep CADbuildr auth on. Your backend mints, your bundle just receives the token.

POST https://hub.cadbuildr.com/api/v1/sdk/session-tokens
Authorization: Bearer cbsdk_acme_5f8a…       ← full secret
Content-Type: application/json
 
{
  "projectId":  "lego",
  "origin":     "https://store.acme.com",
  "endUserId":  "anon-7a3c…",                  // optional, for analytics
  "ttlSeconds": 1800
}
{
  "token":     "cbv1.eyJzdWIiOiJhbm9uLTdhM2Mi…",
  "expiresAt": 1779433200,
  "mode":      "secret"
}

The browser passes the token through to the SDK:

<CadbuildrProvider
  sessionToken={tokenFromYourBackend}
  projectKey="lego"
>
  <CadbuildrViewer dag={dag} />
</CadbuildrProvider>

In server-flow mode the SDK does not refresh tokens — your backend owns the lifecycle. When kernel-api returns 401 token_expired, ask your backend for a fresh token and pass the new value.

The cbv1.* payload

{
  "sub":     "anon-7a3c",
  "iss":     "hub",
  "aud":     "kernel-api",
  "partner": "<partner-id>",
  "project": "lego",
  "origin":  "https://store.acme.com",
  "jti":     "01HZ7C…",
  "budget":  500,
  "iat":     1779431400,
  "exp":     1779433200
}

aud is hardcoded to kernel-api; tokens minted for other audiences are rejected. origin must exactly match the browser’s Origin header on every request. jti is unique per token and is the rate-limit / revocation key in Redis.

Error responses

HTTPcodeMeaning
400Browser flow with no Origin header (likely a cURL call missing the flag).
401Missing both auth modes, or the cbsdk secret didn’t match the key hash.
403origin not in allowed_origins, or projectId not in allowed_projects.
422ttlSeconds out of bounds, or browser flow’s body.origin disagrees with the Origin header.

Local development

For the browser flow, keep your keyId in an env var and let the SDK talk to the default production hub:

# .env.local
VITE_CADBUILDR_SDK_KEY_ID=7ef7b4133ef1e6c2

then in code:

<CadbuildrProvider
  keyId={import.meta.env.VITE_CADBUILDR_SDK_KEY_ID}
  projectKey="lego"
>

</CadbuildrProvider>

Add your local dev origin — e.g. http://localhost:3000 (or whatever port your dev server uses) — to the SDK key’s allowed_origins so the mint call accepts the request. If CADbuildr has provided you a staging hub, point at it with the hubBaseUrl prop.

For the server flow, mint a token once and bake it as VITE_CADBUILDR_SESSION_TOKEN. This is fine for short iterations but the token expires every 30 min; the browser flow doesn’t have that limitation.