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
clientSecretandwidgetSecret, 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-tokenUsage
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, andorganizationReferencefrom your authenticated session, never from the request body.organizationReferencenames the business the user belongs to, so bind it once inconfigureCreateWidgetTokenonly 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>
| Param | Type | Required | Description |
|---|---|---|---|
clientId | string | yes | Tesouro-issued client identifier. Used as JWE kid and as iss/client_id in the payload. |
clientSecret | string | yes | Tesouro-issued client secret. Carried as client_secret inside the encrypted payload. |
widgetSecret | string | yes | Shared symmetric key used to encrypt the JWE. |
userId | string | yes | Subject (sub) of the inner identity token. |
userEmail | string | yes | Email claim of the inner identity token. |
organizationReference | string | yes | Partner-resolved organization/business reference. Carried as organization_reference in the outer encrypted payload. Resolve this server-side, not from request body input. |
expirationInSeconds | number | no | Token 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.