Widget tokens

Server-side helpers for minting widget tokens used by Tesouro's embedded components.

Server-side helpers for minting widget tokens used by Tesouro's embedded components. Mints an RFC 8693 token-exchange request and encrypts it as a JWE (A256KW + A256GCM) using your shared widget secret.

Server-only. This package handles your clientSecret and widgetSecret, so keep it on the server and never bundle it into client code. It runs in any server runtime — a Next.js Route Handler, an Express server, or a Lambda handler.

Installation

yarn add @tesouro/embedded-components-widget-token
# or
npm install @tesouro/embedded-components-widget-token

Usage

One-shot

import { createWidgetToken } from '@tesouro/embedded-components-widget-token';

const { widgetToken, exp } = await createWidgetToken({
  clientId: process.env.TESOURO_CLIENT_ID!,
  clientSecret: process.env.TESOURO_CLIENT_SECRET!,
  widgetSecret: process.env.TESOURO_WIDGET_SECRET!,
  userId: 'user_123',
  userEmail: 'user@example.com',
  organizationReference: 'partner-org-123',
});

exp is the token's expiration time in seconds since epoch — return it to the client so it can refresh before expiry.

Pre-configured factory

When the same credentials are reused across many requests, bind them once with configureCreateWidgetToken:

import { configureCreateWidgetToken } from '@tesouro/embedded-components-widget-token';

const mintWidgetToken = configureCreateWidgetToken({
  clientId: process.env.TESOURO_CLIENT_ID!,
  clientSecret: process.env.TESOURO_CLIENT_SECRET!,
  widgetSecret: process.env.TESOURO_WIDGET_SECRET!,
  organizationReference: 'partner-org-123',
});

const { widgetToken, exp } = await mintWidgetToken({
  userId: 'user_123',
  userEmail: 'user@example.com',
});

In a route handler

Call the factory from an authenticated route on your server and return { widgetToken, exp } to the browser. Getting started has a complete Next.js route handler and the provider that calls it.

Identity must come from the server, not the caller. Resolve userId, userEmail, and organizationReference from your authenticated session, never from the request body. organizationReference names the business the user belongs to, so bind it once in configureCreateWidgetToken only when every user of that server belongs to the same business. Your route mints a valid token for whatever identity it receives, so trusting client-supplied identity lets any caller impersonate another user.

API

createWidgetToken(params): Promise<WidgetTokenResult>

ParamTypeRequiredDescription
clientIdstringyesTesouro-issued client identifier. Used as JWE kid and as iss/client_id in the payload.
clientSecretstringyesTesouro-issued client secret. Carried as client_secret inside the encrypted payload.
widgetSecretstringyesShared symmetric key used to encrypt the JWE.
userIdstringyesSubject (sub) of the inner identity token.
userEmailstringyesEmail claim of the inner identity token.
organizationReferencestringyesPartner-resolved organization/business reference. Carried as organization_reference in the outer encrypted payload. Resolve this server-side, not from request body input.
expirationInSecondsnumbernoToken TTL in seconds. Defaults to DEFAULT_EXPIRATION_IN_SECONDS (480 — 8 minutes).

Returns:

interface WidgetTokenResult {
  widgetToken: string; // compact-serialized JWE
  exp: number; // expiration in seconds since epoch (UTC)
}

Throws if clientId, clientSecret, widgetSecret, or organizationReference is missing or empty.

configureCreateWidgetToken(creds)

Binds clientId, clientSecret, widgetSecret, and optional defaults for organizationReference and expirationInSeconds, and returns a function that only takes per-request userId / userEmail plus optional per-call overrides for organizationReference and expirationInSeconds. organizationReference is still required: the returned function throws unless it is bound here or passed per call.

DEFAULT_EXPIRATION_IN_SECONDS

480 — the default token lifetime in seconds (8 minutes).

Token format

createWidgetToken produces a JWE with the following protected header:

{
  "alg": "A256KW",
  "enc": "A256GCM",
  "kid": "<clientId>",
  "typ": "JWT",
  "cty": "JWT"
}

The encrypted payload is an RFC 8693 token-exchange request whose subject_token is an unsigned identity JWT (alg: none) bearing the user's sub, email, iss, aud, iat, and exp claims. organizationReference is minted at the outer encrypted payload level as organization_reference; it is not added to the inner subject_token.

Backends that don't run Node build the same token from the widget token protocol in Authentication › User token, in the Embedded Banking or FinOps docs.

On this page