@orangecheck/auth-client 2.17.1 → 2.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -31,6 +31,7 @@ interface OcSessionState {
31
31
  status: OcSessionStatus;
32
32
  account: OcAccount | null;
33
33
  roster: OcAccountSummary[];
34
+ tabPinned: boolean;
34
35
  error: Error | null;
35
36
  refresh: () => Promise<void>;
36
37
  signOut: (opts?: {
@@ -152,6 +153,20 @@ interface OcLinkedIdentitiesProps {
152
153
  }
153
154
  declare function OcLinkedIdentities({ authOrigin, className, onChange, }: OcLinkedIdentitiesProps): React.ReactElement;
154
155
 
156
+ declare const TAB_SESSION_HEADER = "x-oc-tab-session";
157
+ declare const TAB_SESSION_STORAGE_KEY = "oc_tab_session";
158
+ declare const TAB_ADOPT_HASH = "#oc-adopt";
159
+ interface OcTabSession {
160
+ token: string;
161
+ didOc: string;
162
+ }
163
+ declare function readTabSession(): OcTabSession | null;
164
+ declare function writeTabSession(session: OcTabSession): void;
165
+ declare function clearTabSession(): void;
166
+ declare function tabSessionHeader(): Record<string, string>;
167
+ declare function installTabFetchInterceptor(authOrigin: string): () => void;
168
+ declare function consumeTabAdoptMarker(): boolean;
169
+
155
170
  interface WebAuthnCredentialPublic {
156
171
  id: string;
157
172
  label: string;
@@ -234,4 +249,4 @@ declare function handleSudoRequired(body: {
234
249
  returnTo?: string;
235
250
  }): boolean;
236
251
 
237
- export { DEFAULT_CONFIG, DISPLAY_IDENTITY_KINDS, type DisplayIdentity, type DisplayIdentityKind, type OcAccount, OcAccountChip, type OcAccountChipProps, OcAccountPill, type OcAccountPillProps, type OcAccountSummary, OcAddressInput, type OcAddressInputProps, type OcAuthConfig, OcLinkedIdentities, type OcLinkedIdentitiesProps, type OcLinkedIdentity, OcSessionProvider, type OcSessionState, type OcSessionStatus, OcSignIn, OcSignInButton, type OcSignInButtonProps, type OcSignInProps, type OcSignOutScope, type RedirectToSudoArgs, type UseOcAddressSuggestionOptions, type UseOcAddressSuggestionReturn, type UseStepUpAuthReturn, type UseWebAuthnListReturn, type UseWebAuthnRegisterReturn, type WebAuthnAssertionStatus, type WebAuthnCredentialPublic, type WebAuthnListStatus, type WebAuthnRegisterResult, type WebAuthnRegisterStatus, type WebAuthnRemoveResult, type WebAuthnRenameResult, type WebAuthnStepUpResult, buildAddAccountUrl, buildSignInUrl, fetchOcLinkedIdentities, handleSudoRequired, redirectToSudo, useOcAddressSuggestion, useOcSession, useOptionalOcSession, useStepUpAuth, useWebAuthnList, useWebAuthnRegister };
252
+ export { DEFAULT_CONFIG, DISPLAY_IDENTITY_KINDS, type DisplayIdentity, type DisplayIdentityKind, type OcAccount, OcAccountChip, type OcAccountChipProps, OcAccountPill, type OcAccountPillProps, type OcAccountSummary, OcAddressInput, type OcAddressInputProps, type OcAuthConfig, OcLinkedIdentities, type OcLinkedIdentitiesProps, type OcLinkedIdentity, OcSessionProvider, type OcSessionState, type OcSessionStatus, OcSignIn, OcSignInButton, type OcSignInButtonProps, type OcSignInProps, type OcSignOutScope, type OcTabSession, type RedirectToSudoArgs, TAB_ADOPT_HASH, TAB_SESSION_HEADER, TAB_SESSION_STORAGE_KEY, type UseOcAddressSuggestionOptions, type UseOcAddressSuggestionReturn, type UseStepUpAuthReturn, type UseWebAuthnListReturn, type UseWebAuthnRegisterReturn, type WebAuthnAssertionStatus, type WebAuthnCredentialPublic, type WebAuthnListStatus, type WebAuthnRegisterResult, type WebAuthnRegisterStatus, type WebAuthnRemoveResult, type WebAuthnRenameResult, type WebAuthnStepUpResult, buildAddAccountUrl, buildSignInUrl, clearTabSession, consumeTabAdoptMarker, fetchOcLinkedIdentities, handleSudoRequired, installTabFetchInterceptor, readTabSession, redirectToSudo, tabSessionHeader, useOcAddressSuggestion, useOcSession, useOptionalOcSession, useStepUpAuth, useWebAuthnList, useWebAuthnRegister, writeTabSession };
package/dist/index.d.ts CHANGED
@@ -31,6 +31,7 @@ interface OcSessionState {
31
31
  status: OcSessionStatus;
32
32
  account: OcAccount | null;
33
33
  roster: OcAccountSummary[];
34
+ tabPinned: boolean;
34
35
  error: Error | null;
35
36
  refresh: () => Promise<void>;
36
37
  signOut: (opts?: {
@@ -152,6 +153,20 @@ interface OcLinkedIdentitiesProps {
152
153
  }
153
154
  declare function OcLinkedIdentities({ authOrigin, className, onChange, }: OcLinkedIdentitiesProps): React.ReactElement;
154
155
 
156
+ declare const TAB_SESSION_HEADER = "x-oc-tab-session";
157
+ declare const TAB_SESSION_STORAGE_KEY = "oc_tab_session";
158
+ declare const TAB_ADOPT_HASH = "#oc-adopt";
159
+ interface OcTabSession {
160
+ token: string;
161
+ didOc: string;
162
+ }
163
+ declare function readTabSession(): OcTabSession | null;
164
+ declare function writeTabSession(session: OcTabSession): void;
165
+ declare function clearTabSession(): void;
166
+ declare function tabSessionHeader(): Record<string, string>;
167
+ declare function installTabFetchInterceptor(authOrigin: string): () => void;
168
+ declare function consumeTabAdoptMarker(): boolean;
169
+
155
170
  interface WebAuthnCredentialPublic {
156
171
  id: string;
157
172
  label: string;
@@ -234,4 +249,4 @@ declare function handleSudoRequired(body: {
234
249
  returnTo?: string;
235
250
  }): boolean;
236
251
 
237
- export { DEFAULT_CONFIG, DISPLAY_IDENTITY_KINDS, type DisplayIdentity, type DisplayIdentityKind, type OcAccount, OcAccountChip, type OcAccountChipProps, OcAccountPill, type OcAccountPillProps, type OcAccountSummary, OcAddressInput, type OcAddressInputProps, type OcAuthConfig, OcLinkedIdentities, type OcLinkedIdentitiesProps, type OcLinkedIdentity, OcSessionProvider, type OcSessionState, type OcSessionStatus, OcSignIn, OcSignInButton, type OcSignInButtonProps, type OcSignInProps, type OcSignOutScope, type RedirectToSudoArgs, type UseOcAddressSuggestionOptions, type UseOcAddressSuggestionReturn, type UseStepUpAuthReturn, type UseWebAuthnListReturn, type UseWebAuthnRegisterReturn, type WebAuthnAssertionStatus, type WebAuthnCredentialPublic, type WebAuthnListStatus, type WebAuthnRegisterResult, type WebAuthnRegisterStatus, type WebAuthnRemoveResult, type WebAuthnRenameResult, type WebAuthnStepUpResult, buildAddAccountUrl, buildSignInUrl, fetchOcLinkedIdentities, handleSudoRequired, redirectToSudo, useOcAddressSuggestion, useOcSession, useOptionalOcSession, useStepUpAuth, useWebAuthnList, useWebAuthnRegister };
252
+ export { DEFAULT_CONFIG, DISPLAY_IDENTITY_KINDS, type DisplayIdentity, type DisplayIdentityKind, type OcAccount, OcAccountChip, type OcAccountChipProps, OcAccountPill, type OcAccountPillProps, type OcAccountSummary, OcAddressInput, type OcAddressInputProps, type OcAuthConfig, OcLinkedIdentities, type OcLinkedIdentitiesProps, type OcLinkedIdentity, OcSessionProvider, type OcSessionState, type OcSessionStatus, OcSignIn, OcSignInButton, type OcSignInButtonProps, type OcSignInProps, type OcSignOutScope, type OcTabSession, type RedirectToSudoArgs, TAB_ADOPT_HASH, TAB_SESSION_HEADER, TAB_SESSION_STORAGE_KEY, type UseOcAddressSuggestionOptions, type UseOcAddressSuggestionReturn, type UseStepUpAuthReturn, type UseWebAuthnListReturn, type UseWebAuthnRegisterReturn, type WebAuthnAssertionStatus, type WebAuthnCredentialPublic, type WebAuthnListStatus, type WebAuthnRegisterResult, type WebAuthnRegisterStatus, type WebAuthnRemoveResult, type WebAuthnRenameResult, type WebAuthnStepUpResult, buildAddAccountUrl, buildSignInUrl, clearTabSession, consumeTabAdoptMarker, fetchOcLinkedIdentities, handleSudoRequired, installTabFetchInterceptor, readTabSession, redirectToSudo, tabSessionHeader, useOcAddressSuggestion, useOcSession, useOptionalOcSession, useStepUpAuth, useWebAuthnList, useWebAuthnRegister, writeTabSession };
package/dist/index.js CHANGED
@@ -49,6 +49,91 @@ function buildAddAccountUrl(cfg, returnTo) {
49
49
  u.searchParams.set("add", "1");
50
50
  return u.toString();
51
51
  }
52
+
53
+ // src/tab-session.ts
54
+ var TAB_SESSION_HEADER = "x-oc-tab-session";
55
+ var TAB_SESSION_STORAGE_KEY = "oc_tab_session";
56
+ var TAB_ADOPT_HASH = "#oc-adopt";
57
+ function readTabSession() {
58
+ if (typeof window === "undefined") return null;
59
+ try {
60
+ const raw = window.sessionStorage.getItem(TAB_SESSION_STORAGE_KEY);
61
+ if (!raw) return null;
62
+ const parsed = JSON.parse(raw);
63
+ if (typeof parsed.token !== "string" || parsed.token.length === 0) return null;
64
+ if (typeof parsed.didOc !== "string" || parsed.didOc.length === 0) return null;
65
+ return { token: parsed.token, didOc: parsed.didOc };
66
+ } catch {
67
+ return null;
68
+ }
69
+ }
70
+ function writeTabSession(session) {
71
+ if (typeof window === "undefined") return;
72
+ try {
73
+ window.sessionStorage.setItem(TAB_SESSION_STORAGE_KEY, JSON.stringify(session));
74
+ } catch {
75
+ }
76
+ }
77
+ function clearTabSession() {
78
+ if (typeof window === "undefined") return;
79
+ try {
80
+ window.sessionStorage.removeItem(TAB_SESSION_STORAGE_KEY);
81
+ } catch {
82
+ }
83
+ }
84
+ function tabSessionHeader() {
85
+ const pin = readTabSession();
86
+ return pin ? { [TAB_SESSION_HEADER]: pin.token } : {};
87
+ }
88
+ function isPinnableUrl(url, authOrigin) {
89
+ if (typeof window === "undefined") return false;
90
+ try {
91
+ const u = new URL(url, window.location.href);
92
+ return u.origin === window.location.origin || u.origin === authOrigin;
93
+ } catch {
94
+ return false;
95
+ }
96
+ }
97
+ function installTabFetchInterceptor(authOrigin) {
98
+ if (typeof window === "undefined") return () => {
99
+ };
100
+ const original = window.fetch;
101
+ const wrapped = (input, init) => {
102
+ try {
103
+ const pin = readTabSession();
104
+ if (pin) {
105
+ const url = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
106
+ if (isPinnableUrl(url, authOrigin)) {
107
+ const headers = new Headers(
108
+ init?.headers ?? (input instanceof Request ? input.headers : void 0)
109
+ );
110
+ if (!headers.has("authorization") && !headers.has(TAB_SESSION_HEADER)) {
111
+ headers.set(TAB_SESSION_HEADER, pin.token);
112
+ init = { ...init, headers };
113
+ }
114
+ }
115
+ }
116
+ } catch {
117
+ }
118
+ return original.call(window, input, init);
119
+ };
120
+ window.fetch = wrapped;
121
+ return () => {
122
+ if (window.fetch === wrapped) window.fetch = original;
123
+ };
124
+ }
125
+ function consumeTabAdoptMarker() {
126
+ if (typeof window === "undefined") return false;
127
+ if (!window.location.hash.includes(TAB_ADOPT_HASH.slice(1))) return false;
128
+ clearTabSession();
129
+ try {
130
+ const url = new URL(window.location.href);
131
+ url.hash = "";
132
+ window.history.replaceState(window.history.state, "", url.toString());
133
+ } catch {
134
+ }
135
+ return true;
136
+ }
52
137
  var SessionContext = React3__namespace.createContext(null);
53
138
  function normalizeDisplayIdentity(raw, didOc) {
54
139
  const di = raw.display_identity ?? raw.displayIdentity;
@@ -70,6 +155,20 @@ function normalizeRosterEntry(raw) {
70
155
  lastSeenAt: raw.last_seen_at ?? raw.lastSeenAt ?? null
71
156
  };
72
157
  }
158
+ async function fetchHostRoster(cfg) {
159
+ try {
160
+ const res = await fetch(`${cfg.authOrigin}${cfg.mePath}`, {
161
+ method: "GET",
162
+ credentials: "include",
163
+ headers: { Accept: "application/json", ...tabSessionHeader() }
164
+ });
165
+ if (!res.ok) return [];
166
+ const body = await res.json();
167
+ return Array.isArray(body.roster) ? body.roster.map(normalizeRosterEntry).filter((r) => r !== null) : [];
168
+ } catch {
169
+ return [];
170
+ }
171
+ }
73
172
  function normalizeAccount(raw) {
74
173
  if (!raw) return null;
75
174
  const didOc = raw.did_oc ?? raw.didOc;
@@ -98,14 +197,50 @@ function OcSessionProvider({
98
197
  const [roster, setRoster] = React3__namespace.useState([]);
99
198
  const [status, setStatus] = React3__namespace.useState("loading");
100
199
  const [error, setError] = React3__namespace.useState(null);
200
+ const [tabPinned, setTabPinned] = React3__namespace.useState(false);
201
+ const pinInFlightRef = React3__namespace.useRef(false);
202
+ const pinThisTab = React3__namespace.useCallback(
203
+ async (didOc) => {
204
+ if (pinInFlightRef.current || readTabSession()) return;
205
+ pinInFlightRef.current = true;
206
+ try {
207
+ const res = await fetch(`${cfg.authOrigin}/api/auth/tab`, {
208
+ method: "POST",
209
+ credentials: "include",
210
+ headers: { Accept: "application/json" }
211
+ });
212
+ if (!res.ok) return;
213
+ const body = await res.json();
214
+ if (body.ok && typeof body.token === "string" && body.account?.did_oc === didOc) {
215
+ writeTabSession({ token: body.token, didOc });
216
+ setTabPinned(true);
217
+ }
218
+ } catch {
219
+ } finally {
220
+ pinInFlightRef.current = false;
221
+ }
222
+ },
223
+ [cfg.authOrigin]
224
+ );
101
225
  const refresh = React3__namespace.useCallback(async () => {
102
226
  if (typeof window === "undefined") return;
103
227
  try {
104
- const res = await fetch(cfg.mePath, {
228
+ let pin = readTabSession();
229
+ let res = await fetch(cfg.mePath, {
105
230
  method: "GET",
106
231
  credentials: "include",
107
- headers: { Accept: "application/json" }
232
+ headers: { Accept: "application/json", ...tabSessionHeader() }
108
233
  });
234
+ if (res.status === 401 && pin) {
235
+ clearTabSession();
236
+ pin = null;
237
+ setTabPinned(false);
238
+ res = await fetch(cfg.mePath, {
239
+ method: "GET",
240
+ credentials: "include",
241
+ headers: { Accept: "application/json" }
242
+ });
243
+ }
109
244
  if (res.status === 401) {
110
245
  setAccount(null);
111
246
  setRoster([]);
@@ -120,19 +255,32 @@ function OcSessionProvider({
120
255
  }
121
256
  const body = await res.json();
122
257
  const acct = normalizeAccount(body.account);
258
+ if (acct && pin && acct.didOc !== pin.didOc) {
259
+ clearTabSession();
260
+ pin = null;
261
+ }
262
+ setTabPinned(pin !== null);
123
263
  const rosterEntries = Array.isArray(body.roster) ? body.roster.map(normalizeRosterEntry).filter((r) => r !== null) : [];
124
264
  setAccount(acct);
125
265
  setRoster(rosterEntries);
126
266
  setStatus(acct ? "authenticated" : "anonymous");
127
267
  setError(null);
268
+ if (acct && !pin) void pinThisTab(acct.didOc);
269
+ const currentOrigin = typeof window !== "undefined" ? window.location.origin : null;
270
+ if (acct && rosterEntries.length === 0 && currentOrigin !== null && !cfg.authOrigin.startsWith(currentOrigin)) {
271
+ const peers = await fetchHostRoster(cfg);
272
+ if (peers.length > 0) setRoster(peers);
273
+ }
128
274
  } catch (err) {
129
275
  setStatus("error");
130
276
  setError(err instanceof Error ? err : new Error(String(err)));
131
277
  }
132
- }, [cfg.mePath]);
278
+ }, [cfg, pinThisTab]);
133
279
  React3__namespace.useEffect(() => {
280
+ consumeTabAdoptMarker();
134
281
  void refresh();
135
282
  }, [refresh]);
283
+ React3__namespace.useEffect(() => installTabFetchInterceptor(cfg.authOrigin), [cfg.authOrigin]);
136
284
  const signOut = React3__namespace.useCallback(
137
285
  async (opts) => {
138
286
  const scope = opts?.scope ?? "all";
@@ -142,6 +290,10 @@ function OcSessionProvider({
142
290
  const res = await fetch(url.toString(), {
143
291
  method: "POST",
144
292
  credentials: "include",
293
+ // Per-tab · carry the pin so scope='current' signs out
294
+ // the account THIS tab is operating as, not whichever
295
+ // account the shared cookie happens to point at.
296
+ headers: { ...tabSessionHeader() },
145
297
  // `keepalive` lets the logout round-trip complete even if
146
298
  // the caller hard-navigates away in the same tick (e.g.
147
299
  // `<OcAccountMenu>` redirects home immediately on sign-out).
@@ -149,11 +301,15 @@ function OcSessionProvider({
149
301
  // and the `.ochk.io` cookie may never get cleared.
150
302
  keepalive: true
151
303
  });
304
+ clearTabSession();
305
+ setTabPinned(false);
152
306
  if (scope === "current" && res.ok) {
153
307
  await refresh();
154
308
  return;
155
309
  }
156
310
  } catch {
311
+ clearTabSession();
312
+ setTabPinned(false);
157
313
  }
158
314
  setAccount(null);
159
315
  setRoster([]);
@@ -167,7 +323,7 @@ function OcSessionProvider({
167
323
  const res = await fetch(`${cfg.authOrigin}/api/auth/switch`, {
168
324
  method: "POST",
169
325
  credentials: "include",
170
- headers: { "Content-Type": "application/json" },
326
+ headers: { "Content-Type": "application/json", ...tabSessionHeader() },
171
327
  body: JSON.stringify({ did_oc: didOc })
172
328
  });
173
329
  if (!res.ok) {
@@ -179,6 +335,14 @@ function OcSessionProvider({
179
335
  }
180
336
  throw new Error(`[@orangecheck/auth-client] switchAccount failed: ${reason}`);
181
337
  }
338
+ try {
339
+ const body = await res.json();
340
+ if (typeof body.token === "string" && body.account?.did_oc) {
341
+ writeTabSession({ token: body.token, didOc: body.account.did_oc });
342
+ setTabPinned(true);
343
+ }
344
+ } catch {
345
+ }
182
346
  await refresh();
183
347
  },
184
348
  [cfg.authOrigin, refresh]
@@ -196,7 +360,7 @@ function OcSessionProvider({
196
360
  const res = await fetch(`${cfg.authOrigin}/api/auth/account`, {
197
361
  method: "PATCH",
198
362
  credentials: "include",
199
- headers: { "Content-Type": "application/json" },
363
+ headers: { "Content-Type": "application/json", ...tabSessionHeader() },
200
364
  body: JSON.stringify({ display_identity: kind })
201
365
  });
202
366
  if (!res.ok) {
@@ -208,6 +372,14 @@ function OcSessionProvider({
208
372
  }
209
373
  throw new Error(`[@orangecheck/auth-client] setDisplayIdentity failed: ${reason}`);
210
374
  }
375
+ try {
376
+ const body = await res.json();
377
+ const pin = readTabSession();
378
+ if (pin && typeof body.token === "string" && body.account?.did_oc === pin.didOc) {
379
+ writeTabSession({ token: body.token, didOc: pin.didOc });
380
+ }
381
+ } catch {
382
+ }
211
383
  await refresh();
212
384
  },
213
385
  [cfg.authOrigin, refresh]
@@ -218,6 +390,7 @@ function OcSessionProvider({
218
390
  status,
219
391
  account,
220
392
  roster,
393
+ tabPinned,
221
394
  error,
222
395
  refresh,
223
396
  signOut,
@@ -230,6 +403,7 @@ function OcSessionProvider({
230
403
  status,
231
404
  account,
232
405
  roster,
406
+ tabPinned,
233
407
  error,
234
408
  refresh,
235
409
  signOut,
@@ -1755,6 +1929,18 @@ function safeReturnTo(input) {
1755
1929
  if (!candidate.startsWith("/") || candidate.startsWith("//")) return "/";
1756
1930
  return candidate;
1757
1931
  }
1932
+ function familyReturnTarget(input) {
1933
+ if (typeof input !== "string" || input.length === 0) return void 0;
1934
+ if (input.startsWith("/") && !input.startsWith("//")) return input;
1935
+ try {
1936
+ const u = new URL(input);
1937
+ if (u.protocol !== "https:") return void 0;
1938
+ const host = u.hostname.toLowerCase();
1939
+ if (host === "ochk.io" || host.endsWith(".ochk.io")) return u.toString();
1940
+ } catch {
1941
+ }
1942
+ return void 0;
1943
+ }
1758
1944
  function hardNavigate(target) {
1759
1945
  if (typeof window === "undefined") return;
1760
1946
  window.location.assign(target);
@@ -1773,7 +1959,20 @@ function OcSignIn({
1773
1959
  }) {
1774
1960
  const walletEnabled = paths?.wallet ?? true;
1775
1961
  const emailEnabled = paths?.email ?? true;
1776
- const safeReturn = safeReturnTo(returnTo);
1962
+ const [resolvedReturn, setResolvedReturn] = React3__namespace.useState(
1963
+ () => familyReturnTarget(returnTo) ?? safeReturnTo(returnTo)
1964
+ );
1965
+ React3__namespace.useEffect(() => {
1966
+ const fromProp = familyReturnTarget(returnTo);
1967
+ if (fromProp) {
1968
+ setResolvedReturn(fromProp);
1969
+ return;
1970
+ }
1971
+ if (typeof window === "undefined") return;
1972
+ const q = new URLSearchParams(window.location.search);
1973
+ const fromQuery = familyReturnTarget(q.get("return_to")) ?? familyReturnTarget(q.get("next"));
1974
+ setResolvedReturn(fromQuery ?? "/");
1975
+ }, [returnTo]);
1777
1976
  const [addMode, setAddMode] = React3__namespace.useState(Boolean(addProp));
1778
1977
  React3__namespace.useEffect(() => {
1779
1978
  if (typeof window === "undefined") return;
@@ -1797,18 +1996,20 @@ function OcSignIn({
1797
1996
  async (account) => {
1798
1997
  if (resolveReturnTo) {
1799
1998
  try {
1800
- hardNavigate(safeReturnTo(await resolveReturnTo(account)));
1999
+ const resolved = familyReturnTarget(await resolveReturnTo(account));
2000
+ hardNavigate(resolved ?? resolvedReturn);
1801
2001
  return;
1802
2002
  } catch {
1803
2003
  }
1804
2004
  }
1805
- hardNavigate(safeReturn);
2005
+ hardNavigate(resolvedReturn);
1806
2006
  },
1807
- [resolveReturnTo, safeReturn]
2007
+ [resolveReturnTo, resolvedReturn]
1808
2008
  );
1809
2009
  const handleSuccess = React3__namespace.useCallback(
1810
2010
  async (account, token, via) => {
1811
2011
  const proceed = () => {
2012
+ clearTabSession();
1812
2013
  if (onSuccess) onSuccess(account, token);
1813
2014
  else void navigate(account);
1814
2015
  };
@@ -1973,7 +2174,7 @@ function OcSignIn({
1973
2174
  }
1974
2175
  )
1975
2176
  ] }),
1976
- /* @__PURE__ */ jsxRuntime.jsx(ProviderSignIn, { authOrigin, returnTo: safeReturn }),
2177
+ /* @__PURE__ */ jsxRuntime.jsx(ProviderSignIn, { authOrigin, returnTo: resolvedReturn, add: addMode }),
1977
2178
  linkPrompt && /* @__PURE__ */ jsxRuntime.jsxs("label", { "data-oc-signin-linkalso": "", style: linkAlsoStyle, children: [
1978
2179
  /* @__PURE__ */ jsxRuntime.jsx(
1979
2180
  "input",
@@ -2043,7 +2244,8 @@ function ProviderIcon({ id }) {
2043
2244
  }
2044
2245
  function ProviderSignIn({
2045
2246
  authOrigin,
2046
- returnTo
2247
+ returnTo,
2248
+ add
2047
2249
  }) {
2048
2250
  const [providers, setProviders] = React3__namespace.useState([]);
2049
2251
  const [origin, setOrigin] = React3__namespace.useState("");
@@ -2059,7 +2261,7 @@ function ProviderSignIn({
2059
2261
  };
2060
2262
  }, [authOrigin]);
2061
2263
  if (providers.length === 0) return null;
2062
- const providerReturnTo = origin ? `${origin}${returnTo}` : returnTo;
2264
+ const providerReturnTo = returnTo.startsWith("/") ? origin ? `${origin}${returnTo}` : returnTo : returnTo;
2063
2265
  const line = { flex: 1, height: 1, background: "var(--border, #27272a)" };
2064
2266
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-oc-signin-providers": "", style: { marginTop: 20 }, children: [
2065
2267
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -2088,7 +2290,7 @@ function ProviderSignIn({
2088
2290
  {
2089
2291
  href: `${authOrigin}/api/auth/${p.id}/start?return_to=${encodeURIComponent(
2090
2292
  providerReturnTo
2091
- )}`,
2293
+ )}${add ? "&add=1" : ""}`,
2092
2294
  "data-oc-signin-provider": p.id,
2093
2295
  style: {
2094
2296
  display: "flex",
@@ -2778,16 +2980,25 @@ exports.OcLinkedIdentities = OcLinkedIdentities;
2778
2980
  exports.OcSessionProvider = OcSessionProvider;
2779
2981
  exports.OcSignIn = OcSignIn;
2780
2982
  exports.OcSignInButton = OcSignInButton;
2983
+ exports.TAB_ADOPT_HASH = TAB_ADOPT_HASH;
2984
+ exports.TAB_SESSION_HEADER = TAB_SESSION_HEADER;
2985
+ exports.TAB_SESSION_STORAGE_KEY = TAB_SESSION_STORAGE_KEY;
2781
2986
  exports.buildAddAccountUrl = buildAddAccountUrl;
2782
2987
  exports.buildSignInUrl = buildSignInUrl;
2988
+ exports.clearTabSession = clearTabSession;
2989
+ exports.consumeTabAdoptMarker = consumeTabAdoptMarker;
2783
2990
  exports.fetchOcLinkedIdentities = fetchOcLinkedIdentities;
2784
2991
  exports.handleSudoRequired = handleSudoRequired;
2992
+ exports.installTabFetchInterceptor = installTabFetchInterceptor;
2993
+ exports.readTabSession = readTabSession;
2785
2994
  exports.redirectToSudo = redirectToSudo;
2995
+ exports.tabSessionHeader = tabSessionHeader;
2786
2996
  exports.useOcAddressSuggestion = useOcAddressSuggestion;
2787
2997
  exports.useOcSession = useOcSession;
2788
2998
  exports.useOptionalOcSession = useOptionalOcSession;
2789
2999
  exports.useStepUpAuth = useStepUpAuth;
2790
3000
  exports.useWebAuthnList = useWebAuthnList;
2791
3001
  exports.useWebAuthnRegister = useWebAuthnRegister;
3002
+ exports.writeTabSession = writeTabSession;
2792
3003
  //# sourceMappingURL=index.js.map
2793
3004
  //# sourceMappingURL=index.js.map