@orangecheck/auth-client 2.27.0 → 2.29.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.mjs CHANGED
@@ -10,7 +10,8 @@ var DEFAULT_CONFIG = {
10
10
  authOrigin: "https://ochk.io",
11
11
  signInPath: "/signin",
12
12
  mePath: "/api/auth/me",
13
- logoutPath: "/api/auth/logout"
13
+ logoutPath: "/api/auth/logout",
14
+ multiAccount: true
14
15
  };
15
16
  function resolveConfig(cfg) {
16
17
  return { ...DEFAULT_CONFIG, ...cfg ?? {} };
@@ -266,7 +267,7 @@ function OcSessionProvider({
266
267
  const pinInFlightRef = React4.useRef(false);
267
268
  const pinThisTab = React4.useCallback(
268
269
  async (didOc) => {
269
- if (pinInFlightRef.current || readTabSession()) return;
270
+ if (!cfg.multiAccount || pinInFlightRef.current || readTabSession()) return;
270
271
  pinInFlightRef.current = true;
271
272
  try {
272
273
  const res = await fetch(`${cfg.authOrigin}/api/auth/tab`, {
@@ -285,7 +286,7 @@ function OcSessionProvider({
285
286
  pinInFlightRef.current = false;
286
287
  }
287
288
  },
288
- [cfg.authOrigin]
289
+ [cfg.authOrigin, cfg.multiAccount]
289
290
  );
290
291
  const refresh = React4.useCallback(async () => {
291
292
  if (typeof window === "undefined") return;
@@ -332,7 +333,7 @@ function OcSessionProvider({
332
333
  setError(null);
333
334
  if (acct && !pin) void pinThisTab(acct.didOc);
334
335
  const currentOrigin = typeof window !== "undefined" ? window.location.origin : null;
335
- if (acct && rosterEntries.length === 0 && currentOrigin !== null && !cfg.authOrigin.startsWith(currentOrigin)) {
336
+ if (cfg.multiAccount && acct && rosterEntries.length === 0 && currentOrigin !== null && !cfg.authOrigin.startsWith(currentOrigin)) {
336
337
  const peers = await fetchHostRoster(cfg);
337
338
  if (peers.length > 0) setRoster(peers);
338
339
  }
@@ -345,15 +346,21 @@ function OcSessionProvider({
345
346
  let cancelled = false;
346
347
  consumeTabAdoptMarker();
347
348
  void (async () => {
348
- await consumeTabAccountHint(cfg.authOrigin);
349
+ if (cfg.multiAccount) await consumeTabAccountHint(cfg.authOrigin);
349
350
  if (!cancelled) void refresh();
350
351
  })();
351
352
  return () => {
352
353
  cancelled = true;
353
354
  };
354
- }, [refresh, cfg.authOrigin]);
355
- React4.useEffect(() => installTabFetchInterceptor(cfg.authOrigin), [cfg.authOrigin]);
356
- React4.useEffect(() => installTabLinkDecorator(cfg.authOrigin), [cfg.authOrigin]);
355
+ }, [refresh, cfg.authOrigin, cfg.multiAccount]);
356
+ React4.useEffect(
357
+ () => cfg.multiAccount ? installTabFetchInterceptor(cfg.authOrigin) : void 0,
358
+ [cfg.authOrigin, cfg.multiAccount]
359
+ );
360
+ React4.useEffect(
361
+ () => cfg.multiAccount ? installTabLinkDecorator(cfg.authOrigin) : void 0,
362
+ [cfg.authOrigin, cfg.multiAccount]
363
+ );
357
364
  const signOut = React4.useCallback(
358
365
  async (opts) => {
359
366
  const scope = opts?.scope ?? "all";
@@ -1167,6 +1174,7 @@ var signinSize = (px) => `calc(${px}px * var(--oc-signin-scale, 1))`;
1167
1174
  var signinRadius = (px) => `var(--oc-signin-radius, ${px}px)`;
1168
1175
  var SIGNIN_BUTTON_RADIUS = "var(--oc-signin-button-radius, 6px)";
1169
1176
  var SIGNIN_STRONG = "var(--oc-signin-strong, 600)";
1177
+ var SIGNIN_ARROW = "var(--oc-signin-arrow, inline)";
1170
1178
  var DEFAULT_AUTH_ORIGIN = "https://ochk.io";
1171
1179
  async function fetchOcLinkedIdentities(opts) {
1172
1180
  const authOrigin = opts?.authOrigin ?? DEFAULT_AUTH_ORIGIN;
@@ -2120,16 +2128,21 @@ function OcSignIn({
2120
2128
  returnTo,
2121
2129
  onSuccess,
2122
2130
  resolveReturnTo,
2123
- linkPrompt = true,
2131
+ linkPrompt: linkPromptProp = true,
2124
2132
  add: addProp,
2125
2133
  authOrigin = "https://ochk.io",
2126
2134
  initialPath,
2127
2135
  providersFirst = true,
2128
2136
  paths,
2129
- className
2137
+ className,
2138
+ mode,
2139
+ providerHref
2130
2140
  }) {
2131
2141
  const walletEnabled = paths?.wallet ?? true;
2132
2142
  const emailEnabled = paths?.email ?? true;
2143
+ const firstParty = mode === "first-party";
2144
+ const credentials = firstParty ? "omit" : "include";
2145
+ const linkPrompt = linkPromptProp && !firstParty;
2133
2146
  const [resolvedReturn, setResolvedReturn] = React4.useState(
2134
2147
  () => familyReturnTarget(returnTo) ?? safeReturnTo(returnTo)
2135
2148
  );
@@ -2144,15 +2157,19 @@ function OcSignIn({
2144
2157
  const fromQuery = familyReturnTarget(q.get("return_to")) ?? familyReturnTarget(q.get("next"));
2145
2158
  setResolvedReturn(fromQuery ?? "/");
2146
2159
  }, [returnTo]);
2147
- const [addMode, setAddMode] = React4.useState(Boolean(addProp));
2160
+ const [addMode, setAddMode] = React4.useState(Boolean(addProp) && !firstParty);
2148
2161
  React4.useEffect(() => {
2149
2162
  if (typeof window === "undefined") return;
2163
+ if (firstParty) {
2164
+ setAddMode(false);
2165
+ return;
2166
+ }
2150
2167
  if (addProp !== void 0) {
2151
2168
  setAddMode(Boolean(addProp));
2152
2169
  return;
2153
2170
  }
2154
2171
  setAddMode(new URLSearchParams(window.location.search).get("add") === "1");
2155
- }, [addProp]);
2172
+ }, [addProp, firstParty]);
2156
2173
  const [path, setPath] = React4.useState(
2157
2174
  initialPath ?? (providersFirst ? "email" : "wallet")
2158
2175
  );
@@ -2309,6 +2326,9 @@ function OcSignIn({
2309
2326
  authOrigin,
2310
2327
  returnTo: resolvedReturn,
2311
2328
  add: addMode,
2329
+ credentials,
2330
+ firstParty,
2331
+ providerHref,
2312
2332
  first: true
2313
2333
  }
2314
2334
  ),
@@ -2330,7 +2350,7 @@ function OcSignIn({
2330
2350
  {
2331
2351
  active: path === "email",
2332
2352
  onClick: () => setPath("email"),
2333
- children: "email"
2353
+ children: "Email"
2334
2354
  }
2335
2355
  ),
2336
2356
  /* @__PURE__ */ jsx(
@@ -2338,7 +2358,7 @@ function OcSignIn({
2338
2358
  {
2339
2359
  active: path === "wallet",
2340
2360
  onClick: () => setPath("wallet"),
2341
- children: "bitcoin wallet"
2361
+ children: "Bitcoin wallet"
2342
2362
  }
2343
2363
  )
2344
2364
  ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -2347,7 +2367,7 @@ function OcSignIn({
2347
2367
  {
2348
2368
  active: path === "wallet",
2349
2369
  onClick: () => setPath("wallet"),
2350
- children: "bitcoin wallet"
2370
+ children: "Bitcoin wallet"
2351
2371
  }
2352
2372
  ),
2353
2373
  /* @__PURE__ */ jsx(
@@ -2355,7 +2375,7 @@ function OcSignIn({
2355
2375
  {
2356
2376
  active: path === "email",
2357
2377
  onClick: () => setPath("email"),
2358
- children: "email"
2378
+ children: "Email"
2359
2379
  }
2360
2380
  )
2361
2381
  ] })
@@ -2368,6 +2388,7 @@ function OcSignIn({
2368
2388
  authOrigin,
2369
2389
  audience,
2370
2390
  add: addMode,
2391
+ credentials,
2371
2392
  onSuccess: (a, t) => void handleSuccess(a, t, "wallet")
2372
2393
  }
2373
2394
  ),
@@ -2376,11 +2397,22 @@ function OcSignIn({
2376
2397
  {
2377
2398
  authOrigin,
2378
2399
  add: addMode,
2400
+ credentials,
2379
2401
  onSuccess: (a, t) => void handleSuccess(a, t, "email")
2380
2402
  }
2381
2403
  )
2382
2404
  ] }),
2383
- !providersFirst && /* @__PURE__ */ jsx(ProviderSignIn, { authOrigin, returnTo: resolvedReturn, add: addMode }),
2405
+ !providersFirst && /* @__PURE__ */ jsx(
2406
+ ProviderSignIn,
2407
+ {
2408
+ authOrigin,
2409
+ returnTo: resolvedReturn,
2410
+ add: addMode,
2411
+ credentials,
2412
+ firstParty,
2413
+ providerHref
2414
+ }
2415
+ ),
2384
2416
  linkPrompt && /* @__PURE__ */ jsxs("label", { "data-oc-signin-linkalso": "", style: linkAlsoStyle, children: [
2385
2417
  /* @__PURE__ */ jsx(
2386
2418
  "input",
@@ -2453,14 +2485,19 @@ function ProviderSignIn({
2453
2485
  authOrigin,
2454
2486
  returnTo,
2455
2487
  add,
2488
+ credentials,
2489
+ firstParty,
2490
+ providerHref,
2456
2491
  first = false
2457
2492
  }) {
2458
2493
  const [providers, setProviders] = React4.useState(null);
2459
2494
  const [origin, setOrigin] = React4.useState("");
2495
+ const hidden = firstParty && !providerHref;
2460
2496
  React4.useEffect(() => {
2461
2497
  setOrigin(window.location.origin);
2498
+ if (hidden) return;
2462
2499
  let cancelled = false;
2463
- fetch(`${authOrigin}/api/auth/providers`, { credentials: "include" }).then((r) => r.ok ? r.json() : null).then((body) => {
2500
+ fetch(`${authOrigin}/api/auth/providers`, { credentials }).then((r) => r.ok ? r.json() : null).then((body) => {
2464
2501
  if (!cancelled) setProviders(body?.providers ?? []);
2465
2502
  }).catch(() => {
2466
2503
  if (!cancelled) setProviders([]);
@@ -2468,7 +2505,8 @@ function ProviderSignIn({
2468
2505
  return () => {
2469
2506
  cancelled = true;
2470
2507
  };
2471
- }, [authOrigin]);
2508
+ }, [authOrigin, credentials, hidden]);
2509
+ if (hidden) return null;
2472
2510
  if (providers !== null && providers.length === 0) return null;
2473
2511
  const providerReturnTo = returnTo.startsWith("/") ? origin ? `${origin}${returnTo}` : returnTo : returnTo;
2474
2512
  const line = { flex: 1, height: 1, background: "var(--border, #27272a)" };
@@ -2526,7 +2564,7 @@ function ProviderSignIn({
2526
2564
  const buttons = providers.map((p, i) => /* @__PURE__ */ jsxs(
2527
2565
  "a",
2528
2566
  {
2529
- href: `${authOrigin}/api/auth/${p.id}/start?return_to=${encodeURIComponent(
2567
+ href: providerHref ? providerHref(p.id) : `${authOrigin}/api/auth/${p.id}/start?return_to=${encodeURIComponent(
2530
2568
  providerReturnTo
2531
2569
  )}${add ? "&add=1" : ""}`,
2532
2570
  "data-oc-signin-provider": p.id,
@@ -2559,7 +2597,13 @@ async function loadAdapter() {
2559
2597
  async function detectBrowserWallets() {
2560
2598
  return (await loadAdapter()).detectWallets().filter((w) => w.detected && w.id !== "manual").map((w) => ({ id: w.id, name: w.name }));
2561
2599
  }
2562
- function WalletFlow({ authOrigin, audience, add, onSuccess }) {
2600
+ function WalletFlow({
2601
+ authOrigin,
2602
+ audience,
2603
+ add,
2604
+ credentials,
2605
+ onSuccess
2606
+ }) {
2563
2607
  const uid = React4.useId();
2564
2608
  const ids = {
2565
2609
  address: `${uid}-address`,
@@ -2601,7 +2645,7 @@ function WalletFlow({ authOrigin, audience, add, onSuccess }) {
2601
2645
  async function submitSignature(message, nonce, signature, scheme) {
2602
2646
  const res = await fetch(`${authOrigin}/api/auth/signin`, {
2603
2647
  method: "POST",
2604
- credentials: "include",
2648
+ credentials,
2605
2649
  headers: { "Content-Type": "application/json" },
2606
2650
  body: JSON.stringify({
2607
2651
  message,
@@ -2693,7 +2737,7 @@ function WalletFlow({ authOrigin, audience, add, onSuccess }) {
2693
2737
  address.trim(),
2694
2738
  ", sign the text below, and paste the signature back here. Nothing is spent."
2695
2739
  ] }),
2696
- /* @__PURE__ */ jsx(Label, { htmlFor: ids.message, children: "message to sign" }),
2740
+ /* @__PURE__ */ jsx(Label, { htmlFor: ids.message, children: "Message to sign" }),
2697
2741
  /* @__PURE__ */ jsx(
2698
2742
  "textarea",
2699
2743
  {
@@ -2705,10 +2749,10 @@ function WalletFlow({ authOrigin, audience, add, onSuccess }) {
2705
2749
  style: { ...inputStyle2, resize: "vertical" }
2706
2750
  }
2707
2751
  ),
2708
- /* @__PURE__ */ jsx("button", { type: "button", onClick: () => void copyMessage(), style: linkButtonStyle, children: copied === "copied" ? "copied" : "copy message" }),
2752
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => void copyMessage(), style: linkButtonStyle, children: copied === "copied" ? "Copied" : "Copy message" }),
2709
2753
  copied === "failed" && /* @__PURE__ */ jsx(Hint, { children: "Couldn't copy here. Select the text above and copy it." }),
2710
2754
  /* @__PURE__ */ jsxs("div", { style: { marginTop: 14 }, children: [
2711
- /* @__PURE__ */ jsx(Label, { htmlFor: ids.signature, children: "signature" }),
2755
+ /* @__PURE__ */ jsx(Label, { htmlFor: ids.signature, children: "Signature" }),
2712
2756
  /* @__PURE__ */ jsx(
2713
2757
  "textarea",
2714
2758
  {
@@ -2734,7 +2778,7 @@ function WalletFlow({ authOrigin, audience, add, onSuccess }) {
2734
2778
  type: "submit",
2735
2779
  disabled: submitting || !pasted.trim(),
2736
2780
  style: submitStyle(submitting || !pasted.trim()),
2737
- children: submitting ? "checking\u2026" : "sign me in \u2192"
2781
+ children: submitting ? "Checking\u2026" : /* @__PURE__ */ jsx(Submit, { children: "Sign me in" })
2738
2782
  }
2739
2783
  ),
2740
2784
  /* @__PURE__ */ jsx(
@@ -2748,7 +2792,7 @@ function WalletFlow({ authOrigin, audience, add, onSuccess }) {
2748
2792
  setCopied("idle");
2749
2793
  },
2750
2794
  style: linkButtonStyle,
2751
- children: "use a different address"
2795
+ children: "Use a different address"
2752
2796
  }
2753
2797
  )
2754
2798
  ] });
@@ -2756,7 +2800,7 @@ function WalletFlow({ authOrigin, audience, add, onSuccess }) {
2756
2800
  const noBrowserWallet = wallets !== null && wallets.length === 0;
2757
2801
  return /* @__PURE__ */ jsxs("form", { onSubmit: signIn, "data-oc-signin-wallet": "", children: [
2758
2802
  /* @__PURE__ */ jsx(FlowHeader, { children: "Your wallet signs a short message to prove you hold the address. Nothing is spent and your key never leaves the wallet. Works with browser wallets, or any wallet that can sign a message, like Sparrow." }),
2759
- /* @__PURE__ */ jsx(Label, { htmlFor: ids.address, children: "bitcoin address" }),
2803
+ /* @__PURE__ */ jsx(Label, { htmlFor: ids.address, children: "Bitcoin address" }),
2760
2804
  /* @__PURE__ */ jsx(
2761
2805
  "input",
2762
2806
  {
@@ -2777,7 +2821,7 @@ function WalletFlow({ authOrigin, audience, add, onSuccess }) {
2777
2821
  }
2778
2822
  ),
2779
2823
  wallets && wallets.length > 1 && /* @__PURE__ */ jsxs("div", { style: { marginTop: 12 }, children: [
2780
- /* @__PURE__ */ jsx(Label, { htmlFor: ids.wallet, children: "sign with" }),
2824
+ /* @__PURE__ */ jsx(Label, { htmlFor: ids.wallet, children: "Sign with" }),
2781
2825
  /* @__PURE__ */ jsx(
2782
2826
  "select",
2783
2827
  {
@@ -2796,13 +2840,13 @@ function WalletFlow({ authOrigin, audience, add, onSuccess }) {
2796
2840
  type: "submit",
2797
2841
  disabled: submitting || !address.trim(),
2798
2842
  style: submitStyle(submitting || !address.trim()),
2799
- children: submitting ? "waiting for wallet\u2026" : noBrowserWallet ? "get the message to sign \u2192" : "sign in with wallet \u2192"
2843
+ children: submitting ? "Waiting for wallet\u2026" : noBrowserWallet ? /* @__PURE__ */ jsx(Submit, { children: "Get the message to sign" }) : /* @__PURE__ */ jsx(Submit, { children: "Sign in with wallet" })
2800
2844
  }
2801
2845
  ),
2802
2846
  noBrowserWallet && /* @__PURE__ */ jsx(Hint, { children: "No browser wallet found here. You can sign with any wallet and paste." })
2803
2847
  ] });
2804
2848
  }
2805
- function EmailFlow({ authOrigin, add, onSuccess }) {
2849
+ function EmailFlow({ authOrigin, add, credentials, onSuccess }) {
2806
2850
  const uid = React4.useId();
2807
2851
  const [stage, setStage] = React4.useState("enter");
2808
2852
  const [email, setEmail] = React4.useState("");
@@ -2816,7 +2860,7 @@ function EmailFlow({ authOrigin, add, onSuccess }) {
2816
2860
  try {
2817
2861
  const res = await fetch(`${authOrigin}/api/auth/email-otp/start`, {
2818
2862
  method: "POST",
2819
- credentials: "include",
2863
+ credentials,
2820
2864
  headers: { "Content-Type": "application/json" },
2821
2865
  body: JSON.stringify({ email: address })
2822
2866
  });
@@ -2868,7 +2912,7 @@ function EmailFlow({ authOrigin, add, onSuccess }) {
2868
2912
  try {
2869
2913
  const res = await fetch(`${authOrigin}/api/auth/email-otp/verify`, {
2870
2914
  method: "POST",
2871
- credentials: "include",
2915
+ credentials,
2872
2916
  headers: { "Content-Type": "application/json" },
2873
2917
  body: JSON.stringify({
2874
2918
  email,
@@ -2894,7 +2938,7 @@ function EmailFlow({ authOrigin, add, onSuccess }) {
2894
2938
  if (stage === "enter") {
2895
2939
  return /* @__PURE__ */ jsxs("form", { onSubmit: start, "data-oc-signin-email": "", children: [
2896
2940
  /* @__PURE__ */ jsx(FlowHeader, { children: "We email you a 6-digit code. No password, and no wallet needed to start. Link a Bitcoin address any time \u2014 that is the identity you hold yourself." }),
2897
- /* @__PURE__ */ jsx(Label, { htmlFor: `${uid}-email`, children: "email" }),
2941
+ /* @__PURE__ */ jsx(Label, { htmlFor: `${uid}-email`, children: "Email" }),
2898
2942
  /* @__PURE__ */ jsx(
2899
2943
  "input",
2900
2944
  {
@@ -2913,7 +2957,7 @@ function EmailFlow({ authOrigin, add, onSuccess }) {
2913
2957
  }
2914
2958
  ),
2915
2959
  emailError && /* @__PURE__ */ jsx(ErrorLine2, { children: emailError }),
2916
- /* @__PURE__ */ jsx("button", { type: "submit", disabled: submitting, style: submitStyle(submitting), children: submitting ? "sending\u2026" : "send one-time code \u2192" })
2960
+ /* @__PURE__ */ jsx("button", { type: "submit", disabled: submitting, style: submitStyle(submitting), children: submitting ? "Sending\u2026" : /* @__PURE__ */ jsx(Submit, { children: "Send one-time code" }) })
2917
2961
  ] });
2918
2962
  }
2919
2963
  return /* @__PURE__ */ jsxs("form", { onSubmit: verify, "data-oc-signin-email-verify": "", children: [
@@ -2922,7 +2966,7 @@ function EmailFlow({ authOrigin, add, onSuccess }) {
2922
2966
  /* @__PURE__ */ jsx("span", { style: { color: "var(--foreground, #fafafa)" }, children: email }),
2923
2967
  ". It expires in 10 minutes."
2924
2968
  ] }),
2925
- /* @__PURE__ */ jsx(Label, { htmlFor: `${uid}-code`, children: "one-time code" }),
2969
+ /* @__PURE__ */ jsx(Label, { htmlFor: `${uid}-code`, children: "One-time code" }),
2926
2970
  /* @__PURE__ */ jsx(
2927
2971
  "input",
2928
2972
  {
@@ -2951,7 +2995,7 @@ function EmailFlow({ authOrigin, add, onSuccess }) {
2951
2995
  type: "submit",
2952
2996
  disabled: submitting || code.length !== 6,
2953
2997
  style: submitStyle(submitting || code.length !== 6),
2954
- children: submitting ? "verifying\u2026" : "verify \xB7 sign me in \u2192"
2998
+ children: submitting ? "Verifying\u2026" : /* @__PURE__ */ jsx(Submit, { children: "Verify \xB7 sign me in" })
2955
2999
  }
2956
3000
  ),
2957
3001
  /* @__PURE__ */ jsxs("div", { children: [
@@ -2962,7 +3006,7 @@ function EmailFlow({ authOrigin, add, onSuccess }) {
2962
3006
  onClick: () => void resend(),
2963
3007
  disabled: submitting,
2964
3008
  style: linkButtonStyle,
2965
- children: "send a new code"
3009
+ children: "Send a new code"
2966
3010
  }
2967
3011
  ),
2968
3012
  /* @__PURE__ */ jsx(
@@ -2976,12 +3020,18 @@ function EmailFlow({ authOrigin, add, onSuccess }) {
2976
3020
  setResent(false);
2977
3021
  },
2978
3022
  style: linkButtonStyle,
2979
- children: "use a different email"
3023
+ children: "Use a different email"
2980
3024
  }
2981
3025
  )
2982
3026
  ] })
2983
3027
  ] });
2984
3028
  }
3029
+ function Submit({ children }) {
3030
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
3031
+ children,
3032
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", style: { display: SIGNIN_ARROW }, children: " \u2192" })
3033
+ ] });
3034
+ }
2985
3035
  function FlowHeader({
2986
3036
  label,
2987
3037
  children