Component token refresh
Fetches a widget token from your endpoint and refreshes it before it expires.
Fetches a widget token from your endpoint and refreshes it before it expires. Feed its live token into RootWidgetProvider, or use RefreshingRootWidgetProvider, which does both.
Overview
Mount WidgetTokenRefreshProvider once at the app root with a fetcher. Inside it, read the live token with useWidgetToken() and pass state.widgetToken to RootWidgetProvider (or any WidgetProvider) as widgetToken. When the token rotates, the new value flows through props and descendant widgets refetch automatically.
For the common case of one refresh provider feeding one RootWidgetProvider, use RefreshingRootWidgetProvider instead.
The fetcher
You implement one function that calls your backend and returns the token:
type WidgetTokenFetcher = () => Promise<{
widgetToken: string;
exp?: number;
}>;- It is called on mount,
leadSecondsbefore eachexp, and onrefresh(). - Return
exp, the token's expiry in unix seconds. Without it, no proactive refresh is scheduled. - Throw (or reject) on failure. The provider retries with backoff.
- A result whose
expis already in the past counts as a failure.
Your endpoint creates the token on your server with
@tesouro/embedded-components-widget-token
and can return its { widgetToken, exp } result unchanged.
Quick Start
import {
RefreshingRootWidgetProvider,
type WidgetTokenFetcher,
} from '@tesouro/embedded-components-react';
const fetcher: WidgetTokenFetcher = async () => {
const res = await fetch('/api/widget-token', { method: 'POST' });
if (!res.ok) throw new Error('Widget token request failed');
return res.json();
};
export function App() {
return (
<RefreshingRootWidgetProvider
baseUrl="https://api.tesouro.com"
fetcher={fetcher}
>
<Dashboard />
</RefreshingRootWidgetProvider>
);
}Manual composition
Use the two providers directly when you need to render UI between them, such as a loading splash.
import {
WidgetTokenRefreshProvider,
RootWidgetProvider,
useWidgetToken,
} from '@tesouro/embedded-components-react';
export function App() {
return (
<WidgetTokenRefreshProvider fetcher={fetcher}>
<AppShell />
</WidgetTokenRefreshProvider>
);
}
function AppShell() {
const { state } = useWidgetToken();
if (state.status !== 'ready') return <Loading />;
return (
<RootWidgetProvider
baseUrl="https://api.tesouro.com"
widgetToken={state.widgetToken}
>
<Dashboard />
</RootWidgetProvider>
);
}Behaviour
- While a refresh is in flight or retrying, the last good token stays in use. The state only becomes
erroronce that token'sexphas passed. - The provider doesn't write to the global widget config. Non-React hosts can mirror the token with
onToken={(t) => updateGlobalWidgetConfig({ widgetToken: t })}; in React, pass it through props instead. - The provider emits no analytics. Refresh failures surface only through
useWidgetToken(), so monitor your token endpoint yourself if you need alerts. - A subtree that needs a different token should pass
widgetTokento its ownWidgetProvider. The refresher doesn't touch it. - To stop refreshing (for example on logout), unmount the provider.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
fetcher |
| Required | Returns a token and its exp (unix seconds); see The fetcher. |
leadSeconds | number | 60 | Refresh this many seconds before exp. Lower it for very short-lived tokens. |
onTokenEvent | (widgetToken: string) => void | undefined | Called once per distinct token, for telemetry, persistence, or mirroring the token into the global store for non-React hosts. |
childrenSlot | ReactNode | undefined | The subtree that reads the token via useWidgetToken(). |
useWidgetToken()
Returns the token state from the nearest WidgetTokenRefreshProvider. Throws outside one.
state: the currentWidgetTokenState(below). Use it to feedRootWidgetProvideror show status.refresh(): forces a refresh and resolves when it settles. Only needed for user-initiated retries.getState(): reads the latest state synchronously, for non-React callbacks that need the token right afterawait refresh().
| State | When |
|---|---|
{ status: 'loading' } | Until the first fetch resolves. |
{ status: 'ready', widgetToken, exp?, isRefreshing, lastError? } | A usable token is present. lastError is set when a refresh failed but the current token hasn't yet expired. |
{ status: 'error', error } | The first fetch failed, or the current token expired while refreshes kept failing. |
function TokenStatusBadge() {
const { state, refresh } = useWidgetToken();
if (state.status === 'loading') return <Spinner />;
if (state.status === 'error') {
return <button onClick={refresh}>Retry</button>;
}
return state.isRefreshing ? <Spinner /> : <span>Token current</span>;
}RefreshingRootWidgetProvider
Mounts WidgetTokenRefreshProvider and RootWidgetProvider together and wires the live token into widgetToken. This is the recommended setup. While the token is loading or errored it passes widgetToken={null}, so widget init waits for a real token. Both useWidgetToken() and useWidgetConfig() work in its children.
Props
RefreshingRootWidgetProviderProps is Omit<RootWidgetProviderProps, 'widgetToken'> & WidgetTokenRefreshProviderProps. widgetToken isn't accepted.
| Prop | Source | Notes |
|---|---|---|
baseUrl | RootWidgetProvider | Falls back to the global store when omitted. |
organizationId | RootWidgetProvider | Pass null to clear the org scope; falls back to the global store when omitted. |
configClient | RootWidgetProvider | Optional post-creation hook for the scoped HTTP client. |
gatewayRouting | RootWidgetProvider | Overrides widget-gateway routing; unset, it applies automatically for known Tesouro API hosts. |
linkComponent | RootWidgetProvider | Component used in place of plain <a> tags inside widgets. |
uiFramework | RootWidgetProvider | 'shadcn' (default) or 'tecton'; inherits down the provider cascade. |
analytics | RootWidgetProvider | Enables anonymous analytics (default true); set false to disable capture. |
unstable_initResponseOverride | RootWidgetProvider | First-party hosts only. Skips the init fetch and supplies host-authored identity for the subtree. |
disclosuresAcceptance | RootWidgetProvider | Accept surface shown when the user owes disclosures. Cascades to nested providers. |
fetcher | WidgetTokenRefreshProvider | Required. |
leadSeconds | WidgetTokenRefreshProvider | Default 60. |
onTokenEvent | WidgetTokenRefreshProvider | Fires once per distinct token. |
children | Both | Rendered inside both providers. |
Label overrides
| Prop | Source | Notes |
|---|---|---|
providerLabelsLabels | RootWidgetProvider | Overrides the copy a provider renders in place of a widget (error fallback, disclosures gate). Cascades. |
Non-React hosts: @tesouro/embedded-components-react/core
The /core subpath exports createWidgetTokenManager with no React runtime, for web-component shells and vanilla-JS embeds. Subscribe first, then call start(). On teardown, call the returned unsubscribe() and stop().
import { createWidgetTokenManager } from '@tesouro/embedded-components-react/core';
import { updateGlobalWidgetConfig } from '@tesouro/embedded-components-react';
const manager = createWidgetTokenManager({ fetcher, leadSeconds: 60 });
const unsubscribe = manager.subscribe(() => {
const state = manager.getState();
if (state.status === 'ready') {
updateGlobalWidgetConfig({ widgetToken: state.widgetToken });
}
});
manager.start();The manager exposes start(), stop(), refresh(), getState() and subscribe(listener). /core also exports the CreateWidgetTokenManagerOptions, WidgetTokenFetcher, WidgetTokenManager and WidgetTokenState types.
TypeScript
import type {
WidgetTokenRefreshProviderProps,
WidgetTokenFetcher,
WidgetTokenManager,
WidgetTokenState,
} from '@tesouro/embedded-components-react';