/** * Hosts that have their own auth model (cookie sessions, app-specific * JWT in localStorage, OAuth access tokens, …) can register an adapter * to override the lib's default `embedProxyAuth` flow. When set, the * adapter's `getHeaders()` result is merged onto every `embedAuthedFetch` * call AFTER the default proxy-auth header step (so adapter headers * win over both caller and proxy values), and `credentials` overrides * the default `'same-origin'` behaviour. * * Default (no adapter): MPH-style proxy-impersonation — bearer + act-as * read from localStorage, `credentials: 'same-origin'`. No consumer * needs to touch this unless they want a different auth model. * * Use cases: * - openframe-frontend has its own JWT in `localStorage.of_access_token` * and cookie-based session; register an adapter to attach the JWT * and request `credentials: 'include'` so cookies travel cross-origin * to the openframe gateway. * - Future embed hosts with OAuth access tokens, signed URLs, etc. * * Lifetime: setter is module-level (intentionally — `embedAuthedFetch` * is a plain utility, not a hook, so it can't read React context). Host * runtime providers should call `setEmbedAuthAdapter(...)` on mount and * `setEmbedAuthAdapter(null)` on unmount. Multiple hosts registering at * once is a programming error (one chat panel per app). */ export interface EmbedAuthAdapter { /** Headers merged onto every embedded-fetch call. Return `{}` to add * nothing. Called per-request so reactive token refresh sees the latest * value from your auth store / storage. Values typed as * `string | undefined` so the common narrowed shape * `{ Authorization: token ? 'Bearer …' : undefined }` (or a conditional * `token ? { Authorization: … } : {}`) assigns cleanly — `undefined` * values are filtered before being merged into the request headers. */ getHeaders?: () => Record; /** `RequestInit.credentials` mode. Default when no adapter: callers' * `init.credentials` or `'same-origin'`. Use `'include'` for cookie * auth against a different origin (CORS + `SameSite=None` required). */ credentials?: RequestCredentials; /** * Optional 401 self-heal. When a request comes back `401`, * `embedAuthedFetch` calls this once, and — if it resolves `true` — * retries the SAME request exactly once with freshly-recomputed * headers (so a rotated bearer from `getHeaders()` is picked up). * Resolve `false` to surface the 401 to the caller unchanged. * * This is the capability the openframe `apiClient` has had all along * (refresh-the-access-token-then-retry); registering it here gives the * embedded chat/ticket surfaces the same self-healing auth instead of * dying on an expired token. Concurrent 401s are de-duplicated by the * wrapper, so this fires at most once per refresh cycle even when a * stampede of chat requests all expire together — your implementation * does NOT need its own in-flight guard (though a token-refresh manager * that already dedups is harmless). * * Keep it idempotent and side-effect-light: on failure the wrapper just * returns the original 401 — logout/redirect decisions belong to the * host's own auth layer, not to this fetch wrapper. */ refresh?: () => Promise; /** * Exact extra origins (`scheme://host[:port]`, as produced by `URL.origin`) * the same-origin guard additionally accepts. The guard normally rejects * every cross-origin URL in production — but native-shell hosts serve the * page from a local pseudo-origin (`capacitor://localhost`, * `tauri://localhost`) with NO server behind it, so the gateway their * bearer already belongs to is unavoidably cross-origin. Listing it here * is the host's explicit sanction to send the adapter's credentials there. * Compared AFTER the http(s)-protocol check, so non-http(s) schemes can * never be allowlisted. Omit (or leave empty) everywhere else — same-origin * remains the rule, this is the narrow exception. */ allowedOrigins?: string[]; } /** * Register a host-owned auth adapter for `embedAuthedFetch`. Pass `null` * to clear (typically on provider unmount). * * Module-level state — there is one chat panel per app, so a single * registration is sufficient. Calling this twice with different non-null * adapters replaces the previous one (the most recent registration wins); * a `console.warn` flags the overwrite so duplicate-provider mounts get * caught in dev. */ export declare function setEmbedAuthAdapter(adapter: EmbedAuthAdapter | null): void; /** * Whether a host auth adapter is currently registered. Lets sibling helpers * (e.g. `contentFetch`) route through `embedAuthedFetch` ONLY when a host has * opted into embedded auth, and stay a plain `fetch` otherwise — so there's a * single auth knob (the adapter), not a second content-fetch registration. */ export declare function hasEmbedAuthAdapter(): boolean; /** * Whether `url` is an asset the browser CANNOT load natively because its * auth rides in request headers. * * Native asset loads (``, ``, CSS `background-image`) * can't carry custom headers, so a URL this returns `true` for must go * through `embedAuthedFetch` → blob object-URL instead (see * `useAuthedAssetSrc` / `useAuthedImageSrc`). * * The single signal is the registered adapter: when it supplies an * `Authorization` header, the host authenticates by HEADER, so nothing the * browser fetches on its own is authenticated. That holds for BOTH shapes a * header-auth host takes: * * - a cross-origin gateway URL — sanctioned via `allowedOrigins` (native * shells, whose page origin is `capacitor://` / `tauri://`); * - a same-origin / relative reverse-proxy path (`/content/api/...`) — * dev-ticket web, where the token lives in localStorage and there is NO * session cookie to fall back on. This case used to be excluded on the * assumption that same-origin implies "cookies work"; true for a cookie * session, false for every header-auth host, which is exactly when this * predicate is consulted at all. * * Everything else loads natively, unchanged: cookie-auth web (the adapter * supplies no `Authorization`), no adapter at all (the hub), and third-party * origins the bearer does NOT belong to (public images) — those stay out of * reach of the token by the same origin rules `embedAuthedFetch` enforces. */ export declare function needsBearerAssetFetch(url: string): boolean; /** * `fetch` wrapper that attaches embed-proxy bearer headers (when * present in sessionStorage) and forces `credentials: 'same-origin'` * so Supabase auth cookies travel too. * * **Header merge direction (proxy WINS over caller):** the implementation * spreads `baseHeaders` first inside `applyProxyAuth`, then sets the * `Authorization` / `X-Chat-*` keys — so the proxy values take precedence * over anything the caller passed. The motivation is that the bearer + * act-as identity is the source of truth for embedded auth; a caller * accidentally passing a stale `Authorization` header should NOT override * the live proxy creds. * * **Cross-origin defense:** the wrapper assumes a same-origin `/api/…` * relative URL. Absolute URLs are accepted only when their origin matches * the current window's origin — or appears in the registered adapter's * `allowedOrigins` (the native-shell hatch; see that field's doc) — and * cross-origin URLs otherwise throw before the bearer leaves the page. * This is a defense-in-depth guard for future call sites — outside the * allowlisted-shell case there is no legitimate cross-origin use of this * fetch wrapper. * * **401 self-heal:** when a registered adapter supplies `refresh`, a `401` * response triggers a single token refresh + retry of the same request * (see `EmbedAuthAdapter.refresh`). This is the openframe `apiClient`'s * refresh-then-retry behaviour, lifted into the lib so embedded surfaces * no longer need a host-side `window.fetch` monkey-patch to survive an * expired access token mid-chat. With no adapter (or no `refresh`), the * 401 passes straight through unchanged. */ export declare function embedAuthedFetch(url: string, init?: RequestInit): Promise; //# sourceMappingURL=embed-authed-fetch.d.ts.map