Identity and targeting

Decide who should see which updates.

Most teams can start with a very small widget token. When you need different customers, plans, or internal users to see different updates, add a few extra claims and let Chainlog handle the filtering for you.

What the widget token does

This token is the handoff between your backend and the widget. It tells Chainlog who the user is and what broad access they have.

import { SignJWT } from "jose";

export async function GET() {
  const user = await getCurrentUserFromYourApp();
  if (!user) return Response.json({ error: "Unauthorized" }, { status: 401 });

  const now = Math.floor(Date.now() / 1000);
  const secret = new TextEncoder().encode(process.env.CHAINLOG_WIDGET_CREDENTIAL_SECRET!);

  const token = await new SignJWT({
    tenant: process.env.CHAINLOG_TENANT_ID!,
    sub: user.id,
    aud: "chainlog-widget",
    kid: process.env.CHAINLOG_WIDGET_CREDENTIAL_ID!,
    entitlements: user.entitlements || [],
    contextModule: user.contextModule || null,
    internalAccess: Boolean(user.canViewInternalUpdates),
    iat: now,
    exp: now + 600,
  })
    .setProtectedHeader({ alg: "HS256", typ: "JWT" })
    .sign(secret);

  return Response.json({ token, expiresInSeconds: 600 });
}
ClaimRequiredWhat it means
tenantRequiredWhich Chainlog workspace the widget is allowed to read from.
subRequiredA stable user ID so Chainlog can evaluate visibility and rate limits correctly.
audRequired, "chainlog-widget"Marks the token as a widget token.
kidRecommendedTells Chainlog which widget credential signed the token.
entitlementsOptionalPlan or capability tags such as pro or enterprise.
internalAccessOptionalLets staff or support users see internal-only updates.
showAllUpdatesOptionalBroadens visibility when your product allows a user to see everything.
contextModuleOptionalA default product area such as billing, platform, or dashboard.
iat / expRequiredIssued-at and expiration timestamps. Keep the token short-lived.

Start simple, then add targeting later

Every widget request is authenticated, but most products do not need a complex claim set right away.

Minimal claim set

Start with tenant, sub, aud, kid, iat, and exp. That is enough for the default signed-in widget setup.

Targeted claim set

Add entitlements, internalAccess, or contextModule only when you need plan-specific, internal-only, or product-area-specific filtering.

Recommended default

Start with tenant, sub, aud, kid, iat, and exp. Add extra claims only when a real audience rule appears.

When to use each targeting claim

Think of claims as coarse access rules. Chainlog combines them with each entry’s own visibility settings.

  • entitlements are best for plan, tier, or capability access such as pro or enterprise.
  • contextModule is best for product-area defaults such as billing, reports, or dashboard.
  • internalAccess should be reserved for employee or support views where internal-only entries are allowed.
  • Tenant runtime defaults control presentation, not access. They can set mode, theme, label, and sizing, but they do not replace entry visibility rules.
window.ChainlogWidget.init({
  tenantId: "CHAINLOG_TENANT_ID",
  useTenantDefaults: true,
  tokenProvider: async () => {
    const response = await fetch("/api/widget-token", {
      credentials: "include",
    });

    const { token, expiresInSeconds } = await response.json();
    return { token, expiresInSeconds };
  },
});

Claims are for access, not presentation

Claims control which entries the widget can fetch. They do not replace tenant runtime defaults for presentation, and they do not create a separate anonymous widget mode.

Safe defaults

These habits keep the auth model simple and predictable in production.

  • Never expose widget credential secrets in browser code, mobile bundles, or public environment variables.
  • Keep widget JWT TTL short. Ten minutes is a good default for logged-in product usage.
  • Rotate widget credentials regularly and update the backend secret manager immediately after rotation.
  • Only include claims that the widget actually needs to evaluate visibility.
  • Handle token minting through your normal app session instead of inventing a second auth flow for the widget.

What to read next