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, leadSeconds before each exp, and on refresh().
  • 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 exp is 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 error once that token's exp has 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 widgetToken to its own WidgetProvider. The refresher doesn't touch it.
  • To stop refreshing (for example on logout), unmount the provider.

Props

PropTypeDefaultDescription
fetcher
() => Promise<{ widgetToken: string; exp?: number }>
() => Promise<{ widgetToken: string; exp?: number }>
RequiredReturns a token and its exp (unix seconds); see The fetcher.
leadSecondsnumber60Refresh this many seconds before exp. Lower it for very short-lived tokens.
onTokenEvent(widgetToken: string) => voidundefinedCalled once per distinct token, for telemetry, persistence, or mirroring the token into the global store for non-React hosts.
childrenSlotReactNodeundefinedThe subtree that reads the token via useWidgetToken().

useWidgetToken()

Returns the token state from the nearest WidgetTokenRefreshProvider. Throws outside one.

  • state: the current WidgetTokenState (below). Use it to feed RootWidgetProvider or 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 after await refresh().
StateWhen
{ 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.

PropSourceNotes
baseUrlRootWidgetProviderFalls back to the global store when omitted.
organizationIdRootWidgetProviderPass null to clear the org scope; falls back to the global store when omitted.
configClientRootWidgetProviderOptional post-creation hook for the scoped HTTP client.
gatewayRoutingRootWidgetProviderOverrides widget-gateway routing; unset, it applies automatically for known Tesouro API hosts.
linkComponentRootWidgetProviderComponent used in place of plain <a> tags inside widgets.
uiFrameworkRootWidgetProvider'shadcn' (default) or 'tecton'; inherits down the provider cascade.
analyticsRootWidgetProviderEnables anonymous analytics (default true); set false to disable capture.
unstable_initResponseOverrideRootWidgetProviderFirst-party hosts only. Skips the init fetch and supplies host-authored identity for the subtree.
disclosuresAcceptanceRootWidgetProviderAccept surface shown when the user owes disclosures. Cascades to nested providers.
fetcherWidgetTokenRefreshProviderRequired.
leadSecondsWidgetTokenRefreshProviderDefault 60.
onTokenEventWidgetTokenRefreshProviderFires once per distinct token.
childrenBothRendered inside both providers.
Label overrides
PropSourceNotes
providerLabelsLabelsRootWidgetProviderOverrides 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';

On this page