Upgrading to v8
v8 changes the requestAttributes nonce parameter from a value to a callback, and adds an opt-in top-level redirect sign-in flow.
requestAttributes nonce is now a callback (breaking)
Section titled “requestAttributes nonce is now a callback (breaking)”AuthClient.requestAttributes previously took the nonce as a Uint8Array or a Promise<Uint8Array>. It now takes a callback that fetches the nonce, returning a Promise<Uint8Array>. Pass the fetching function itself — don’t await it at the call site:
await authClient.requestAttributes({ keys: ['email'], nonce: await fetchNonce(),});await authClient.requestAttributes({ keys: ['email'], nonce: () => fetchNonce(),});The client invokes the callback when it needs the nonce and, in redirect mode, journals the produced value and reuses it when the flow replays after the redirect — so the signer signs against the same single-use nonce rather than a freshly fetched one. Pre-fetching the nonce and wrapping the result (nonce: () => Promise.resolve(alreadyFetched)) re-runs the fetch on every page load and defeats that, so pass the fetch function instead.
The nonce is a callback rather than a value so the redirect flow (below) can journal it and reuse the exact same bytes when the flow replays after the redirect, instead of fetching a fresh single-use nonce the signer never signed against. In the default window flow the behaviour is otherwise unchanged: the callback still lets the identity provider window open while the nonce resolves.
New: top-level redirect sign-in via transport: 'redirect'
Section titled “New: top-level redirect sign-in via transport: 'redirect'”AuthClient gains an optional transport option. It defaults to 'window' — the existing flow, where the identity provider opens in a separate browser tab or window (a popup when windowOpenerFeatures is set) and communicates over ICRC-29 postMessage. Set it to 'redirect' to talk to the identity provider over the ICRC-167 URL transport — a full-page redirect — which suits full-page sign-in flows that shouldn’t need a user gesture to open a separate window — for example redirecting straight to sign-in when someone lands on a restricted page (opening a tab or window requires a user interaction, a redirect does not) — as well as native apps that hand off to the browser via universal links (deep links).
const authClient = new AuthClient({ transport: 'redirect' });
// IMPORTANT: with 'redirect' the page unloads on each step, so this code must// run directly on page load — not deferred behind a click handler. A fresh// visit starts the flow; the identity provider's return replays it to// completion on the reload.const identity = await authClient.signIn();Requirements for 'redirect':
- Run
signIn/requestAttributeson page load. The redirect unloads the page and the flow re-runs when the identity provider returns, so the calls must execute again on that return load — not from an event handler that only fires on user interaction. - The callback URL is the current page’s URL (
location.origin + location.pathname), so that page must be on an origin you control and declared in that origin’s/.well-known/ii-auth-callbacksallow-list. Give each flow its own route so its persisted state stays isolated. - The session key and the attributes nonce survive the redirect automatically — the client persists them and reuses them on the return load.
Carrying your own values across the redirect
Section titled “Carrying your own values across the redirect”Anything you compute on the first visit (from location, a fetch, crypto, …) is recomputed on the return load, since the page re-runs. To keep such a value stable — for example the URL to return to once sign-in completes — wrap it in authClient.memoize, which runs the callback once on the first visit, journals the result, and replays it on the return:
const authClient = new AuthClient({ transport: 'redirect' });
// Captured before the redirect, replayed after — even though location.search// differs on the return load.const next = await authClient.memoize( () => new URLSearchParams(location.search).get('next') ?? '/',);
await authClient.signIn();location.assign(next);Call memoize in a stable order relative to signIn / requestAttributes, and keep its result JSON-serializable. In 'window' mode it simply runs the callback and returns the result, so the same code works with either transport.
Leaving transport unset (or 'window') keeps the previous behaviour exactly; this option is purely additive.