@orangecheck/auth-client 2.17.2 → 2.19.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?: {
@@ -124,6 +125,7 @@ interface OcSignInProps {
124
125
  resolveReturnTo?: (account: OcAccount) => string | Promise<string>;
125
126
  authOrigin?: string;
126
127
  initialPath?: 'wallet' | 'email';
128
+ providersFirst?: boolean;
127
129
  paths?: {
128
130
  wallet?: boolean;
129
131
  email?: boolean;
@@ -132,7 +134,7 @@ interface OcSignInProps {
132
134
  add?: boolean;
133
135
  className?: string;
134
136
  }
135
- declare function OcSignIn({ audience, returnTo, onSuccess, resolveReturnTo, linkPrompt, add: addProp, authOrigin, initialPath, paths, className, }: OcSignInProps): React.ReactElement;
137
+ declare function OcSignIn({ audience, returnTo, onSuccess, resolveReturnTo, linkPrompt, add: addProp, authOrigin, initialPath, providersFirst, paths, className, }: OcSignInProps): React.ReactElement;
136
138
 
137
139
  interface OcLinkedIdentity {
138
140
  id: string;
@@ -152,6 +154,20 @@ interface OcLinkedIdentitiesProps {
152
154
  }
153
155
  declare function OcLinkedIdentities({ authOrigin, className, onChange, }: OcLinkedIdentitiesProps): React.ReactElement;
154
156
 
157
+ declare const TAB_SESSION_HEADER = "x-oc-tab-session";
158
+ declare const TAB_SESSION_STORAGE_KEY = "oc_tab_session";
159
+ declare const TAB_ADOPT_HASH = "#oc-adopt";
160
+ interface OcTabSession {
161
+ token: string;
162
+ didOc: string;
163
+ }
164
+ declare function readTabSession(): OcTabSession | null;
165
+ declare function writeTabSession(session: OcTabSession): void;
166
+ declare function clearTabSession(): void;
167
+ declare function tabSessionHeader(): Record<string, string>;
168
+ declare function installTabFetchInterceptor(authOrigin: string): () => void;
169
+ declare function consumeTabAdoptMarker(): boolean;
170
+
155
171
  interface WebAuthnCredentialPublic {
156
172
  id: string;
157
173
  label: string;
@@ -234,4 +250,4 @@ declare function handleSudoRequired(body: {
234
250
  returnTo?: string;
235
251
  }): boolean;
236
252
 
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 };
253
+ 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?: {
@@ -124,6 +125,7 @@ interface OcSignInProps {
124
125
  resolveReturnTo?: (account: OcAccount) => string | Promise<string>;
125
126
  authOrigin?: string;
126
127
  initialPath?: 'wallet' | 'email';
128
+ providersFirst?: boolean;
127
129
  paths?: {
128
130
  wallet?: boolean;
129
131
  email?: boolean;
@@ -132,7 +134,7 @@ interface OcSignInProps {
132
134
  add?: boolean;
133
135
  className?: string;
134
136
  }
135
- declare function OcSignIn({ audience, returnTo, onSuccess, resolveReturnTo, linkPrompt, add: addProp, authOrigin, initialPath, paths, className, }: OcSignInProps): React.ReactElement;
137
+ declare function OcSignIn({ audience, returnTo, onSuccess, resolveReturnTo, linkPrompt, add: addProp, authOrigin, initialPath, providersFirst, paths, className, }: OcSignInProps): React.ReactElement;
136
138
 
137
139
  interface OcLinkedIdentity {
138
140
  id: string;
@@ -152,6 +154,20 @@ interface OcLinkedIdentitiesProps {
152
154
  }
153
155
  declare function OcLinkedIdentities({ authOrigin, className, onChange, }: OcLinkedIdentitiesProps): React.ReactElement;
154
156
 
157
+ declare const TAB_SESSION_HEADER = "x-oc-tab-session";
158
+ declare const TAB_SESSION_STORAGE_KEY = "oc_tab_session";
159
+ declare const TAB_ADOPT_HASH = "#oc-adopt";
160
+ interface OcTabSession {
161
+ token: string;
162
+ didOc: string;
163
+ }
164
+ declare function readTabSession(): OcTabSession | null;
165
+ declare function writeTabSession(session: OcTabSession): void;
166
+ declare function clearTabSession(): void;
167
+ declare function tabSessionHeader(): Record<string, string>;
168
+ declare function installTabFetchInterceptor(authOrigin: string): () => void;
169
+ declare function consumeTabAdoptMarker(): boolean;
170
+
155
171
  interface WebAuthnCredentialPublic {
156
172
  id: string;
157
173
  label: string;
@@ -234,4 +250,4 @@ declare function handleSudoRequired(body: {
234
250
  returnTo?: string;
235
251
  }): boolean;
236
252
 
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 };
253
+ 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;
@@ -75,7 +160,7 @@ async function fetchHostRoster(cfg) {
75
160
  const res = await fetch(`${cfg.authOrigin}${cfg.mePath}`, {
76
161
  method: "GET",
77
162
  credentials: "include",
78
- headers: { Accept: "application/json" }
163
+ headers: { Accept: "application/json", ...tabSessionHeader() }
79
164
  });
80
165
  if (!res.ok) return [];
81
166
  const body = await res.json();
@@ -112,14 +197,50 @@ function OcSessionProvider({
112
197
  const [roster, setRoster] = React3__namespace.useState([]);
113
198
  const [status, setStatus] = React3__namespace.useState("loading");
114
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
+ );
115
225
  const refresh = React3__namespace.useCallback(async () => {
116
226
  if (typeof window === "undefined") return;
117
227
  try {
118
- const res = await fetch(cfg.mePath, {
228
+ let pin = readTabSession();
229
+ let res = await fetch(cfg.mePath, {
119
230
  method: "GET",
120
231
  credentials: "include",
121
- headers: { Accept: "application/json" }
232
+ headers: { Accept: "application/json", ...tabSessionHeader() }
122
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
+ }
123
244
  if (res.status === 401) {
124
245
  setAccount(null);
125
246
  setRoster([]);
@@ -134,11 +255,17 @@ function OcSessionProvider({
134
255
  }
135
256
  const body = await res.json();
136
257
  const acct = normalizeAccount(body.account);
258
+ if (acct && pin && acct.didOc !== pin.didOc) {
259
+ clearTabSession();
260
+ pin = null;
261
+ }
262
+ setTabPinned(pin !== null);
137
263
  const rosterEntries = Array.isArray(body.roster) ? body.roster.map(normalizeRosterEntry).filter((r) => r !== null) : [];
138
264
  setAccount(acct);
139
265
  setRoster(rosterEntries);
140
266
  setStatus(acct ? "authenticated" : "anonymous");
141
267
  setError(null);
268
+ if (acct && !pin) void pinThisTab(acct.didOc);
142
269
  const currentOrigin = typeof window !== "undefined" ? window.location.origin : null;
143
270
  if (acct && rosterEntries.length === 0 && currentOrigin !== null && !cfg.authOrigin.startsWith(currentOrigin)) {
144
271
  const peers = await fetchHostRoster(cfg);
@@ -148,10 +275,12 @@ function OcSessionProvider({
148
275
  setStatus("error");
149
276
  setError(err instanceof Error ? err : new Error(String(err)));
150
277
  }
151
- }, [cfg]);
278
+ }, [cfg, pinThisTab]);
152
279
  React3__namespace.useEffect(() => {
280
+ consumeTabAdoptMarker();
153
281
  void refresh();
154
282
  }, [refresh]);
283
+ React3__namespace.useEffect(() => installTabFetchInterceptor(cfg.authOrigin), [cfg.authOrigin]);
155
284
  const signOut = React3__namespace.useCallback(
156
285
  async (opts) => {
157
286
  const scope = opts?.scope ?? "all";
@@ -161,6 +290,10 @@ function OcSessionProvider({
161
290
  const res = await fetch(url.toString(), {
162
291
  method: "POST",
163
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() },
164
297
  // `keepalive` lets the logout round-trip complete even if
165
298
  // the caller hard-navigates away in the same tick (e.g.
166
299
  // `<OcAccountMenu>` redirects home immediately on sign-out).
@@ -168,11 +301,15 @@ function OcSessionProvider({
168
301
  // and the `.ochk.io` cookie may never get cleared.
169
302
  keepalive: true
170
303
  });
304
+ clearTabSession();
305
+ setTabPinned(false);
171
306
  if (scope === "current" && res.ok) {
172
307
  await refresh();
173
308
  return;
174
309
  }
175
310
  } catch {
311
+ clearTabSession();
312
+ setTabPinned(false);
176
313
  }
177
314
  setAccount(null);
178
315
  setRoster([]);
@@ -186,7 +323,7 @@ function OcSessionProvider({
186
323
  const res = await fetch(`${cfg.authOrigin}/api/auth/switch`, {
187
324
  method: "POST",
188
325
  credentials: "include",
189
- headers: { "Content-Type": "application/json" },
326
+ headers: { "Content-Type": "application/json", ...tabSessionHeader() },
190
327
  body: JSON.stringify({ did_oc: didOc })
191
328
  });
192
329
  if (!res.ok) {
@@ -198,6 +335,14 @@ function OcSessionProvider({
198
335
  }
199
336
  throw new Error(`[@orangecheck/auth-client] switchAccount failed: ${reason}`);
200
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
+ }
201
346
  await refresh();
202
347
  },
203
348
  [cfg.authOrigin, refresh]
@@ -215,7 +360,7 @@ function OcSessionProvider({
215
360
  const res = await fetch(`${cfg.authOrigin}/api/auth/account`, {
216
361
  method: "PATCH",
217
362
  credentials: "include",
218
- headers: { "Content-Type": "application/json" },
363
+ headers: { "Content-Type": "application/json", ...tabSessionHeader() },
219
364
  body: JSON.stringify({ display_identity: kind })
220
365
  });
221
366
  if (!res.ok) {
@@ -227,6 +372,14 @@ function OcSessionProvider({
227
372
  }
228
373
  throw new Error(`[@orangecheck/auth-client] setDisplayIdentity failed: ${reason}`);
229
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
+ }
230
383
  await refresh();
231
384
  },
232
385
  [cfg.authOrigin, refresh]
@@ -237,6 +390,7 @@ function OcSessionProvider({
237
390
  status,
238
391
  account,
239
392
  roster,
393
+ tabPinned,
240
394
  error,
241
395
  refresh,
242
396
  signOut,
@@ -249,6 +403,7 @@ function OcSessionProvider({
249
403
  status,
250
404
  account,
251
405
  roster,
406
+ tabPinned,
252
407
  error,
253
408
  refresh,
254
409
  signOut,
@@ -1774,6 +1929,18 @@ function safeReturnTo(input) {
1774
1929
  if (!candidate.startsWith("/") || candidate.startsWith("//")) return "/";
1775
1930
  return candidate;
1776
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
+ }
1777
1944
  function hardNavigate(target) {
1778
1945
  if (typeof window === "undefined") return;
1779
1946
  window.location.assign(target);
@@ -1786,13 +1953,27 @@ function OcSignIn({
1786
1953
  linkPrompt = true,
1787
1954
  add: addProp,
1788
1955
  authOrigin = "https://ochk.io",
1789
- initialPath = "wallet",
1956
+ initialPath,
1957
+ providersFirst = false,
1790
1958
  paths,
1791
1959
  className
1792
1960
  }) {
1793
1961
  const walletEnabled = paths?.wallet ?? true;
1794
1962
  const emailEnabled = paths?.email ?? true;
1795
- const safeReturn = safeReturnTo(returnTo);
1963
+ const [resolvedReturn, setResolvedReturn] = React3__namespace.useState(
1964
+ () => familyReturnTarget(returnTo) ?? safeReturnTo(returnTo)
1965
+ );
1966
+ React3__namespace.useEffect(() => {
1967
+ const fromProp = familyReturnTarget(returnTo);
1968
+ if (fromProp) {
1969
+ setResolvedReturn(fromProp);
1970
+ return;
1971
+ }
1972
+ if (typeof window === "undefined") return;
1973
+ const q = new URLSearchParams(window.location.search);
1974
+ const fromQuery = familyReturnTarget(q.get("return_to")) ?? familyReturnTarget(q.get("next"));
1975
+ setResolvedReturn(fromQuery ?? "/");
1976
+ }, [returnTo]);
1796
1977
  const [addMode, setAddMode] = React3__namespace.useState(Boolean(addProp));
1797
1978
  React3__namespace.useEffect(() => {
1798
1979
  if (typeof window === "undefined") return;
@@ -1802,7 +1983,9 @@ function OcSignIn({
1802
1983
  }
1803
1984
  setAddMode(new URLSearchParams(window.location.search).get("add") === "1");
1804
1985
  }, [addProp]);
1805
- const [path, setPath] = React3__namespace.useState(initialPath);
1986
+ const [path, setPath] = React3__namespace.useState(
1987
+ initialPath ?? (providersFirst ? "email" : "wallet")
1988
+ );
1806
1989
  const [signedIn, setSignedIn] = React3__namespace.useState(null);
1807
1990
  const [linkAlso, setLinkAlso] = React3__namespace.useState(false);
1808
1991
  const [oauthError, setOauthError] = React3__namespace.useState(false);
@@ -1816,18 +1999,20 @@ function OcSignIn({
1816
1999
  async (account) => {
1817
2000
  if (resolveReturnTo) {
1818
2001
  try {
1819
- hardNavigate(safeReturnTo(await resolveReturnTo(account)));
2002
+ const resolved = familyReturnTarget(await resolveReturnTo(account));
2003
+ hardNavigate(resolved ?? resolvedReturn);
1820
2004
  return;
1821
2005
  } catch {
1822
2006
  }
1823
2007
  }
1824
- hardNavigate(safeReturn);
2008
+ hardNavigate(resolvedReturn);
1825
2009
  },
1826
- [resolveReturnTo, safeReturn]
2010
+ [resolveReturnTo, resolvedReturn]
1827
2011
  );
1828
2012
  const handleSuccess = React3__namespace.useCallback(
1829
2013
  async (account, token, via) => {
1830
2014
  const proceed = () => {
2015
+ clearTabSession();
1831
2016
  if (onSuccess) onSuccess(account, token);
1832
2017
  else void navigate(account);
1833
2018
  };
@@ -1948,7 +2133,16 @@ function OcSignIn({
1948
2133
  ]
1949
2134
  }
1950
2135
  ),
1951
- bothEnabled && /* @__PURE__ */ jsxRuntime.jsxs(
2136
+ providersFirst && /* @__PURE__ */ jsxRuntime.jsx(
2137
+ ProviderSignIn,
2138
+ {
2139
+ authOrigin,
2140
+ returnTo: resolvedReturn,
2141
+ add: addMode,
2142
+ first: true
2143
+ }
2144
+ ),
2145
+ bothEnabled && /* @__PURE__ */ jsxRuntime.jsx(
1952
2146
  "div",
1953
2147
  {
1954
2148
  role: "tablist",
@@ -1960,7 +2154,24 @@ function OcSignIn({
1960
2154
  marginBottom: 16,
1961
2155
  borderBottom: "1px solid var(--border, #27272a)"
1962
2156
  },
1963
- children: [
2157
+ children: providersFirst ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2158
+ /* @__PURE__ */ jsxRuntime.jsx(
2159
+ SigninTab,
2160
+ {
2161
+ active: path === "email",
2162
+ onClick: () => setPath("email"),
2163
+ children: "email + otp"
2164
+ }
2165
+ ),
2166
+ /* @__PURE__ */ jsxRuntime.jsx(
2167
+ SigninTab,
2168
+ {
2169
+ active: path === "wallet",
2170
+ onClick: () => setPath("wallet"),
2171
+ children: "bitcoin \xB7 self-custody"
2172
+ }
2173
+ )
2174
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1964
2175
  /* @__PURE__ */ jsxRuntime.jsx(
1965
2176
  SigninTab,
1966
2177
  {
@@ -1969,8 +2180,15 @@ function OcSignIn({
1969
2180
  children: "bitcoin wallet"
1970
2181
  }
1971
2182
  ),
1972
- /* @__PURE__ */ jsxRuntime.jsx(SigninTab, { active: path === "email", onClick: () => setPath("email"), children: "email + otp" })
1973
- ]
2183
+ /* @__PURE__ */ jsxRuntime.jsx(
2184
+ SigninTab,
2185
+ {
2186
+ active: path === "email",
2187
+ onClick: () => setPath("email"),
2188
+ children: "email + otp"
2189
+ }
2190
+ )
2191
+ ] })
1974
2192
  }
1975
2193
  ),
1976
2194
  /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-oc-signin-panel": "", role: "tabpanel", children: [
@@ -1992,7 +2210,7 @@ function OcSignIn({
1992
2210
  }
1993
2211
  )
1994
2212
  ] }),
1995
- /* @__PURE__ */ jsxRuntime.jsx(ProviderSignIn, { authOrigin, returnTo: safeReturn }),
2213
+ !providersFirst && /* @__PURE__ */ jsxRuntime.jsx(ProviderSignIn, { authOrigin, returnTo: resolvedReturn, add: addMode }),
1996
2214
  linkPrompt && /* @__PURE__ */ jsxRuntime.jsxs("label", { "data-oc-signin-linkalso": "", style: linkAlsoStyle, children: [
1997
2215
  /* @__PURE__ */ jsxRuntime.jsx(
1998
2216
  "input",
@@ -2062,7 +2280,9 @@ function ProviderIcon({ id }) {
2062
2280
  }
2063
2281
  function ProviderSignIn({
2064
2282
  authOrigin,
2065
- returnTo
2283
+ returnTo,
2284
+ add,
2285
+ first = false
2066
2286
  }) {
2067
2287
  const [providers, setProviders] = React3__namespace.useState([]);
2068
2288
  const [origin, setOrigin] = React3__namespace.useState("");
@@ -2078,62 +2298,67 @@ function ProviderSignIn({
2078
2298
  };
2079
2299
  }, [authOrigin]);
2080
2300
  if (providers.length === 0) return null;
2081
- const providerReturnTo = origin ? `${origin}${returnTo}` : returnTo;
2301
+ const providerReturnTo = returnTo.startsWith("/") ? origin ? `${origin}${returnTo}` : returnTo : returnTo;
2082
2302
  const line = { flex: 1, height: 1, background: "var(--border, #27272a)" };
2083
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-oc-signin-providers": "", style: { marginTop: 20 }, children: [
2084
- /* @__PURE__ */ jsxRuntime.jsxs(
2085
- "div",
2086
- {
2087
- style: {
2088
- display: "flex",
2089
- alignItems: "center",
2090
- gap: 10,
2091
- margin: "4px 0 12px",
2092
- color: "var(--muted-foreground, #a1a1aa)",
2093
- fontFamily: "ui-monospace, SFMono-Regular, monospace",
2094
- fontSize: 10,
2095
- letterSpacing: "0.16em",
2096
- textTransform: "uppercase"
2097
- },
2098
- children: [
2099
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: line }),
2100
- "or",
2101
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: line })
2102
- ]
2103
- }
2104
- ),
2105
- providers.map((p, i) => /* @__PURE__ */ jsxRuntime.jsxs(
2106
- "a",
2107
- {
2108
- href: `${authOrigin}/api/auth/${p.id}/start?return_to=${encodeURIComponent(
2109
- providerReturnTo
2110
- )}`,
2111
- "data-oc-signin-provider": p.id,
2112
- style: {
2113
- display: "flex",
2114
- alignItems: "center",
2115
- justifyContent: "center",
2116
- gap: 10,
2117
- boxSizing: "border-box",
2118
- width: "100%",
2119
- marginTop: i === 0 ? 0 : 8,
2120
- padding: "0.6rem 0.875rem",
2121
- border: "1px solid var(--border, #27272a)",
2122
- borderRadius: 6,
2123
- background: "transparent",
2124
- color: "var(--muted-foreground, #a1a1aa)",
2125
- fontFamily: "ui-monospace, SFMono-Regular, monospace",
2126
- fontSize: 12,
2127
- textDecoration: "none"
2128
- },
2129
- children: [
2130
- /* @__PURE__ */ jsxRuntime.jsx(ProviderIcon, { id: p.id }),
2131
- /* @__PURE__ */ jsxRuntime.jsx("span", { children: p.label })
2132
- ]
2303
+ const divider = /* @__PURE__ */ jsxRuntime.jsxs(
2304
+ "div",
2305
+ {
2306
+ style: {
2307
+ display: "flex",
2308
+ alignItems: "center",
2309
+ gap: 10,
2310
+ margin: first ? "14px 0 4px" : "4px 0 12px",
2311
+ color: "var(--muted-foreground, #a1a1aa)",
2312
+ fontFamily: "ui-monospace, SFMono-Regular, monospace",
2313
+ fontSize: 10,
2314
+ letterSpacing: "0.16em",
2315
+ textTransform: "uppercase"
2133
2316
  },
2134
- p.id
2135
- ))
2136
- ] });
2317
+ children: [
2318
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: line }),
2319
+ "or",
2320
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: line })
2321
+ ]
2322
+ }
2323
+ );
2324
+ const buttons = providers.map((p, i) => /* @__PURE__ */ jsxRuntime.jsxs(
2325
+ "a",
2326
+ {
2327
+ href: `${authOrigin}/api/auth/${p.id}/start?return_to=${encodeURIComponent(
2328
+ providerReturnTo
2329
+ )}${add ? "&add=1" : ""}`,
2330
+ "data-oc-signin-provider": p.id,
2331
+ style: {
2332
+ display: "flex",
2333
+ alignItems: "center",
2334
+ justifyContent: "center",
2335
+ gap: 10,
2336
+ boxSizing: "border-box",
2337
+ width: "100%",
2338
+ marginTop: i === 0 ? 0 : 8,
2339
+ padding: "0.6rem 0.875rem",
2340
+ border: "1px solid var(--border, #27272a)",
2341
+ borderRadius: 6,
2342
+ background: "transparent",
2343
+ color: "var(--muted-foreground, #a1a1aa)",
2344
+ fontFamily: "ui-monospace, SFMono-Regular, monospace",
2345
+ fontSize: 12,
2346
+ textDecoration: "none"
2347
+ },
2348
+ children: [
2349
+ /* @__PURE__ */ jsxRuntime.jsx(ProviderIcon, { id: p.id }),
2350
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: p.label })
2351
+ ]
2352
+ },
2353
+ p.id
2354
+ ));
2355
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-oc-signin-providers": "", style: first ? { marginBottom: 4 } : { marginTop: 20 }, children: first ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2356
+ buttons,
2357
+ divider
2358
+ ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2359
+ divider,
2360
+ buttons
2361
+ ] }) });
2137
2362
  }
2138
2363
  function WalletFlow({ authOrigin, audience, add, onSuccess }) {
2139
2364
  const [address, setAddress] = React3__namespace.useState("");
@@ -2797,16 +3022,25 @@ exports.OcLinkedIdentities = OcLinkedIdentities;
2797
3022
  exports.OcSessionProvider = OcSessionProvider;
2798
3023
  exports.OcSignIn = OcSignIn;
2799
3024
  exports.OcSignInButton = OcSignInButton;
3025
+ exports.TAB_ADOPT_HASH = TAB_ADOPT_HASH;
3026
+ exports.TAB_SESSION_HEADER = TAB_SESSION_HEADER;
3027
+ exports.TAB_SESSION_STORAGE_KEY = TAB_SESSION_STORAGE_KEY;
2800
3028
  exports.buildAddAccountUrl = buildAddAccountUrl;
2801
3029
  exports.buildSignInUrl = buildSignInUrl;
3030
+ exports.clearTabSession = clearTabSession;
3031
+ exports.consumeTabAdoptMarker = consumeTabAdoptMarker;
2802
3032
  exports.fetchOcLinkedIdentities = fetchOcLinkedIdentities;
2803
3033
  exports.handleSudoRequired = handleSudoRequired;
3034
+ exports.installTabFetchInterceptor = installTabFetchInterceptor;
3035
+ exports.readTabSession = readTabSession;
2804
3036
  exports.redirectToSudo = redirectToSudo;
3037
+ exports.tabSessionHeader = tabSessionHeader;
2805
3038
  exports.useOcAddressSuggestion = useOcAddressSuggestion;
2806
3039
  exports.useOcSession = useOcSession;
2807
3040
  exports.useOptionalOcSession = useOptionalOcSession;
2808
3041
  exports.useStepUpAuth = useStepUpAuth;
2809
3042
  exports.useWebAuthnList = useWebAuthnList;
2810
3043
  exports.useWebAuthnRegister = useWebAuthnRegister;
3044
+ exports.writeTabSession = writeTabSession;
2811
3045
  //# sourceMappingURL=index.js.map
2812
3046
  //# sourceMappingURL=index.js.map