@tesouro/embedded-components-widget-token
v0.2.167
Published
Server-side helpers for minting widget tokens used by Tesouro's embedded components. Mints an [RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) token-exchange request and encrypts it as a JWE (`A256KW` + `A256GCM`) using your shared widget secret.
Keywords
Readme
@tesouro/embedded-components-widget-token
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: '[email protected]',
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: '[email protected]',
});Next.js Route Handler
// app/api/widget-token/route.ts
import { NextRequest, NextResponse } from 'next/server';
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: process.env.TESOURO_ORGANIZATION_REFERENCE!,
});
export async function POST(req: NextRequest) {
// Resolve the user from YOUR authenticated session — NEVER from the request
// body. This route hands back a valid widget token for whatever identity you
// pass in, so reading `userId` / `userEmail` from `req.json()` would let any
// caller mint a token impersonating someone else. Swap `getSession` for your
// own server-side session verification (signed cookie, bearer token, etc.).
const session = await getSession(req);
if (!session) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const { widgetToken, exp } = await mintWidgetToken({
userId: session.userId,
userEmail: session.userEmail,
});
return NextResponse.json({ widgetToken, exp });
}Identity must come from the server, not the caller. Resolve
userId,userEmail, andorganizationReferencefrom your authenticated session and partner config — never from the request body. This endpoint 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 | no | 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, or widgetSecret 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.
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. When supplied,
organizationReference is minted at the outer encrypted payload level as
organization_reference; it is not added to the inner subject_token.
License
Apache-2.0
