@orangecheck/auth-client 2.5.1 → 2.6.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
@@ -1,5 +1,5 @@
1
- import * as React4 from 'react';
2
- import { jsxs, jsx } from 'react/jsx-runtime';
1
+ import * as React3 from 'react';
2
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
3
3
  import { startRegistration, startAuthentication } from '@simplewebauthn/browser';
4
4
 
5
5
  // src/provider.tsx
@@ -21,7 +21,7 @@ function buildSignInUrl(cfg, returnTo) {
21
21
  u.searchParams.set("return_to", returnTo);
22
22
  return u.toString();
23
23
  }
24
- var SessionContext = React4.createContext(null);
24
+ var SessionContext = React3.createContext(null);
25
25
  function normalizeAccount(raw) {
26
26
  if (!raw) return null;
27
27
  const didOc = raw.did_oc ?? raw.didOc;
@@ -44,11 +44,11 @@ function OcSessionProvider({
44
44
  config,
45
45
  defaultReturnTo
46
46
  }) {
47
- const cfg = React4.useMemo(() => resolveConfig(config), [config]);
48
- const [account, setAccount] = React4.useState(null);
49
- const [status, setStatus] = React4.useState("loading");
50
- const [error, setError] = React4.useState(null);
51
- const refresh = React4.useCallback(async () => {
47
+ const cfg = React3.useMemo(() => resolveConfig(config), [config]);
48
+ const [account, setAccount] = React3.useState(null);
49
+ const [status, setStatus] = React3.useState("loading");
50
+ const [error, setError] = React3.useState(null);
51
+ const refresh = React3.useCallback(async () => {
52
52
  if (typeof window === "undefined") return;
53
53
  try {
54
54
  const res = await fetch(cfg.mePath, {
@@ -77,10 +77,10 @@ function OcSessionProvider({
77
77
  setError(err instanceof Error ? err : new Error(String(err)));
78
78
  }
79
79
  }, [cfg.mePath]);
80
- React4.useEffect(() => {
80
+ React3.useEffect(() => {
81
81
  void refresh();
82
82
  }, [refresh]);
83
- const signOut = React4.useCallback(async () => {
83
+ const signOut = React3.useCallback(async () => {
84
84
  try {
85
85
  await fetch(`${cfg.authOrigin}${cfg.logoutPath}`, {
86
86
  method: "POST",
@@ -91,7 +91,7 @@ function OcSessionProvider({
91
91
  setAccount(null);
92
92
  setStatus("anonymous");
93
93
  }, [cfg.authOrigin, cfg.logoutPath]);
94
- const value = React4.useMemo(() => {
94
+ const value = React3.useMemo(() => {
95
95
  const returnTo = defaultReturnTo ?? (typeof window !== "undefined" ? window.location.href : void 0);
96
96
  return {
97
97
  status,
@@ -105,7 +105,7 @@ function OcSessionProvider({
105
105
  return /* @__PURE__ */ jsx(SessionContext.Provider, { value, children });
106
106
  }
107
107
  function useOcSession() {
108
- const ctx = React4.useContext(SessionContext);
108
+ const ctx = React3.useContext(SessionContext);
109
109
  if (!ctx) {
110
110
  throw new Error(
111
111
  "[@orangecheck/auth-client] useOcSession() must be called inside <OcSessionProvider>"
@@ -114,7 +114,7 @@ function useOcSession() {
114
114
  return ctx;
115
115
  }
116
116
  function useOptionalOcSession() {
117
- return React4.useContext(SessionContext);
117
+ return React3.useContext(SessionContext);
118
118
  }
119
119
  function shortenAddress(addr) {
120
120
  if (addr.length <= 12) return addr;
@@ -129,7 +129,7 @@ function isPrefixOf(value, target) {
129
129
  }
130
130
  var listboxIdCounter = 0;
131
131
  function useUniqueId(prefix) {
132
- const [id] = React4.useState(() => `${prefix}-${++listboxIdCounter}`);
132
+ const [id] = React3.useState(() => `${prefix}-${++listboxIdCounter}`);
133
133
  return id;
134
134
  }
135
135
  function OcAccountChip({
@@ -143,9 +143,9 @@ function OcAccountChip({
143
143
  }) {
144
144
  const { status, account, signInUrl: sessionSignInUrl, signOut } = useOcSession();
145
145
  const signInUrl = signInUrlOverride ?? sessionSignInUrl;
146
- const [open, setOpen] = React4.useState(false);
147
- const wrapRef = React4.useRef(null);
148
- React4.useEffect(() => {
146
+ const [open, setOpen] = React3.useState(false);
147
+ const wrapRef = React3.useRef(null);
148
+ React3.useEffect(() => {
149
149
  if (!open) return;
150
150
  function handleClickOutside(e) {
151
151
  if (wrapRef.current && !wrapRef.current.contains(e.target)) {
@@ -468,9 +468,9 @@ function useOcAddressSuggestion(options) {
468
468
  } = options;
469
469
  const { status, account } = useOcSession();
470
470
  const sessionAddress = status === "authenticated" ? account?.primaryBtc ?? null : null;
471
- const [open, setOpen] = React4.useState(false);
472
- const [highlighted, setHighlighted] = React4.useState(false);
473
- const blurTimer = React4.useRef(null);
471
+ const [open, setOpen] = React3.useState(false);
472
+ const [highlighted, setHighlighted] = React3.useState(false);
473
+ const blurTimer = React3.useRef(null);
474
474
  const listboxId = useUniqueId("oc-addr-listbox");
475
475
  const optionId = `${listboxId}-opt`;
476
476
  const valueMatchesSession = sessionAddress != null && value.toLowerCase() === sessionAddress.toLowerCase();
@@ -482,7 +482,7 @@ function useOcAddressSuggestion(options) {
482
482
  setOpen(false);
483
483
  setHighlighted(false);
484
484
  }
485
- React4.useEffect(() => {
485
+ React3.useEffect(() => {
486
486
  return () => {
487
487
  if (blurTimer.current != null) window.clearTimeout(blurTimer.current);
488
488
  };
@@ -585,7 +585,7 @@ function useOcAddressSuggestion(options) {
585
585
  ) : null;
586
586
  return { inputProps, popover };
587
587
  }
588
- var OcAddressInput = React4.forwardRef(
588
+ var OcAddressInput = React3.forwardRef(
589
589
  function OcAddressInput2({
590
590
  value,
591
591
  onValueChange,
@@ -600,15 +600,15 @@ var OcAddressInput = React4.forwardRef(
600
600
  }, forwardedRef) {
601
601
  const { status, account } = useOcSession();
602
602
  const sessionAddress = status === "authenticated" ? account?.primaryBtc ?? null : null;
603
- const [open, setOpen] = React4.useState(false);
604
- const [highlighted, setHighlighted] = React4.useState(false);
605
- const innerRef = React4.useRef(null);
603
+ const [open, setOpen] = React3.useState(false);
604
+ const [highlighted, setHighlighted] = React3.useState(false);
605
+ const innerRef = React3.useRef(null);
606
606
  const setRef = (node) => {
607
607
  innerRef.current = node;
608
608
  if (typeof forwardedRef === "function") forwardedRef(node);
609
609
  else if (forwardedRef) forwardedRef.current = node;
610
610
  };
611
- const blurTimer = React4.useRef(null);
611
+ const blurTimer = React3.useRef(null);
612
612
  const listboxId = useUniqueId("oc-addr-listbox");
613
613
  const optionId = `${listboxId}-opt`;
614
614
  const valueMatchesSession = sessionAddress != null && value.toLowerCase() === sessionAddress.toLowerCase();
@@ -655,7 +655,7 @@ var OcAddressInput = React4.forwardRef(
655
655
  }
656
656
  onKeyDown?.(e);
657
657
  }
658
- React4.useEffect(() => {
658
+ React3.useEffect(() => {
659
659
  return () => {
660
660
  if (blurTimer.current != null) window.clearTimeout(blurTimer.current);
661
661
  };
@@ -748,6 +748,797 @@ var OcAddressInput = React4.forwardRef(
748
748
  );
749
749
  }
750
750
  );
751
+ var DEFAULT_AUTH_ORIGIN = "https://ochk.io";
752
+ function OcLinkedIdentities({
753
+ authOrigin = DEFAULT_AUTH_ORIGIN,
754
+ className,
755
+ onChange
756
+ }) {
757
+ const [resp, setResp] = React3.useState(null);
758
+ const [error, setError] = React3.useState(null);
759
+ const [showEmailLink, setShowEmailLink] = React3.useState(false);
760
+ const [showBtcLink, setShowBtcLink] = React3.useState(false);
761
+ const refresh = React3.useCallback(async () => {
762
+ setError(null);
763
+ try {
764
+ const r = await fetch(`${authOrigin}/api/auth/identities`, {
765
+ credentials: "include"
766
+ });
767
+ if (!r.ok) {
768
+ if (r.status === 401) {
769
+ setError("not signed in");
770
+ return;
771
+ }
772
+ throw new Error(`http_${r.status}`);
773
+ }
774
+ setResp(await r.json());
775
+ } catch (e) {
776
+ setError(e instanceof Error ? e.message : "load failed");
777
+ }
778
+ }, [authOrigin]);
779
+ React3.useEffect(() => {
780
+ void refresh();
781
+ }, [refresh]);
782
+ const afterChange = React3.useCallback(() => {
783
+ setShowEmailLink(false);
784
+ setShowBtcLink(false);
785
+ void refresh();
786
+ onChange?.();
787
+ }, [refresh, onChange]);
788
+ if (error) {
789
+ return /* @__PURE__ */ jsxs("div", { className, "data-oc-li": "", style: panelStyle("warn"), role: "alert", children: [
790
+ /* @__PURE__ */ jsx(SectionLabel, { tone: "warn", children: "\xA7 identities \xB7 load deferred" }),
791
+ /* @__PURE__ */ jsxs("p", { style: bodyStyle, children: [
792
+ "Couldn't reach the auth host (",
793
+ error,
794
+ "). Linking is temporarily unavailable; your signed-in identity still works as normal."
795
+ ] }),
796
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => void refresh(), style: ghostBtnStyle(false), children: "retry" })
797
+ ] });
798
+ }
799
+ if (!resp) {
800
+ return /* @__PURE__ */ jsx("div", { className, "data-oc-li": "", style: panelStyle(), children: /* @__PURE__ */ jsxs("span", { style: hintStyle, children: [
801
+ "> ",
802
+ "loading\u2026"
803
+ ] }) });
804
+ }
805
+ const linked = resp.linked ?? [];
806
+ const emailRows = linked.filter((i) => i.kind === "email");
807
+ const btcRows = linked.filter((i) => i.kind === "btc");
808
+ const hasEmail = emailRows.some((i) => i.value);
809
+ const hasBtc = btcRows.some((i) => i.value);
810
+ const mergedFrom = resp.merged_from ?? [];
811
+ return /* @__PURE__ */ jsxs("div", { className, "data-oc-li": "", style: { display: "grid", gap: 16 }, children: [
812
+ /* @__PURE__ */ jsxs("div", { style: panelStyle(), children: [
813
+ /* @__PURE__ */ jsx(SectionLabel, { children: "\xA7 what this is" }),
814
+ /* @__PURE__ */ jsxs("p", { style: bodyStyle, children: [
815
+ "Your public identity is the opaque ",
816
+ /* @__PURE__ */ jsx("code", { style: codeStyle, children: "did:oc:" }),
817
+ " DID. Below it sit your private linked identities \u2014 your email and your Bitcoin address. They never appear in dashboard chrome and never reach an integrator unless you grant the matching scope. Linking a second identity is also your account-recovery path: if you lose one, the other still signs you in."
818
+ ] })
819
+ ] }),
820
+ mergedFrom.length > 0 && /* @__PURE__ */ jsx(ConsolidationPanel, { entries: mergedFrom }),
821
+ /* @__PURE__ */ jsxs("div", { style: panelStyle("plain"), "data-oc-li-rows": "", children: [
822
+ /* @__PURE__ */ jsx(
823
+ IdentityGroup,
824
+ {
825
+ kind: "email",
826
+ title: "email",
827
+ rows: emailRows,
828
+ authOrigin,
829
+ onChange: afterChange,
830
+ addCta: hasEmail ? null : "link an email",
831
+ onAdd: () => {
832
+ setShowEmailLink((v) => !v);
833
+ setShowBtcLink(false);
834
+ }
835
+ }
836
+ ),
837
+ /* @__PURE__ */ jsx("div", { style: { borderTop: `1px solid ${V.border}` } }),
838
+ /* @__PURE__ */ jsx(
839
+ IdentityGroup,
840
+ {
841
+ kind: "btc",
842
+ title: "bitcoin address",
843
+ rows: btcRows,
844
+ authOrigin,
845
+ onChange: afterChange,
846
+ addCta: hasBtc ? null : "link a Bitcoin address",
847
+ onAdd: () => {
848
+ setShowBtcLink((v) => !v);
849
+ setShowEmailLink(false);
850
+ }
851
+ }
852
+ )
853
+ ] }),
854
+ showEmailLink && /* @__PURE__ */ jsx(
855
+ EmailLinkForm,
856
+ {
857
+ authOrigin,
858
+ onDone: afterChange,
859
+ onCancel: () => setShowEmailLink(false)
860
+ }
861
+ ),
862
+ showBtcLink && /* @__PURE__ */ jsx(
863
+ BtcLinkForm,
864
+ {
865
+ authOrigin,
866
+ didOc: resp.did_oc ?? null,
867
+ onDone: afterChange,
868
+ onCancel: () => setShowBtcLink(false)
869
+ }
870
+ )
871
+ ] });
872
+ }
873
+ function IdentityGroup({
874
+ kind,
875
+ title,
876
+ rows,
877
+ authOrigin,
878
+ onChange,
879
+ addCta,
880
+ onAdd
881
+ }) {
882
+ if (rows.length === 0) {
883
+ return /* @__PURE__ */ jsxs("div", { style: rowStyle, children: [
884
+ /* @__PURE__ */ jsxs("div", { children: [
885
+ /* @__PURE__ */ jsxs("div", { style: rowLabelStyle, children: [
886
+ "\xA7 ",
887
+ title
888
+ ] }),
889
+ /* @__PURE__ */ jsx("div", { style: { ...bodyStyle, marginTop: 4 }, children: "none linked" })
890
+ ] }),
891
+ addCta && /* @__PURE__ */ jsxs("button", { type: "button", onClick: onAdd, style: primaryChipStyle, children: [
892
+ "+ ",
893
+ addCta
894
+ ] })
895
+ ] });
896
+ }
897
+ return /* @__PURE__ */ jsx(Fragment, { children: rows.map((r) => /* @__PURE__ */ jsx(
898
+ IdentityRow,
899
+ {
900
+ row: r,
901
+ kind,
902
+ authOrigin,
903
+ onChange
904
+ },
905
+ r.id
906
+ )) });
907
+ }
908
+ function IdentityRow({
909
+ row,
910
+ kind,
911
+ authOrigin,
912
+ onChange
913
+ }) {
914
+ const [shown, setShown] = React3.useState(false);
915
+ const [confirming, setConfirming] = React3.useState(false);
916
+ const [busy, setBusy] = React3.useState(false);
917
+ const [err, setErr] = React3.useState(null);
918
+ const masked = row.value ? mask(row.value, kind) : "\u2014 pending recovery \u2014";
919
+ const display = shown && row.value ? row.value : masked;
920
+ async function unlink() {
921
+ if (row.is_primary) return;
922
+ setBusy(true);
923
+ setErr(null);
924
+ try {
925
+ const r = await fetch(
926
+ `${authOrigin}/api/auth/identities?id=${encodeURIComponent(row.id)}`,
927
+ { method: "DELETE", credentials: "include" }
928
+ );
929
+ if (!r.ok) {
930
+ const j = await r.json().catch(() => ({}));
931
+ throw new Error(j.reason ?? `http_${r.status}`);
932
+ }
933
+ onChange();
934
+ } catch (e) {
935
+ setErr(e instanceof Error ? e.message : "unlink failed");
936
+ setBusy(false);
937
+ setConfirming(false);
938
+ }
939
+ }
940
+ return /* @__PURE__ */ jsxs("div", { style: rowStyle, "data-oc-li-row": kind, children: [
941
+ /* @__PURE__ */ jsxs("div", { style: { minWidth: 0, flex: 1 }, children: [
942
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexWrap: "wrap", gap: "4px 12px" }, children: [
943
+ /* @__PURE__ */ jsxs("span", { style: rowLabelStyle, children: [
944
+ "\xA7 ",
945
+ kind === "email" ? "email" : "bitcoin address"
946
+ ] }),
947
+ row.is_primary && /* @__PURE__ */ jsx("span", { style: { ...rowLabelStyle, color: V.primary }, children: "primary \xB7 signup-time" })
948
+ ] }),
949
+ /* @__PURE__ */ jsx(
950
+ "div",
951
+ {
952
+ style: {
953
+ marginTop: 4,
954
+ color: V.foreground,
955
+ fontFamily: MONO,
956
+ fontSize: 12,
957
+ wordBreak: "break-all"
958
+ },
959
+ children: display
960
+ }
961
+ ),
962
+ row.verified_at && /* @__PURE__ */ jsxs("div", { style: { ...hintStyle, marginTop: 4 }, children: [
963
+ "verified \xB7 ",
964
+ new Date(row.verified_at).toUTCString()
965
+ ] }),
966
+ err && /* @__PURE__ */ jsx(ErrorLine, { children: err })
967
+ ] }),
968
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexShrink: 0, gap: 8, flexWrap: "wrap" }, children: [
969
+ row.value && /* @__PURE__ */ jsx(
970
+ "button",
971
+ {
972
+ type: "button",
973
+ onClick: () => setShown((s) => !s),
974
+ style: ghostBtnStyle(false),
975
+ children: shown ? "hide" : "show"
976
+ }
977
+ ),
978
+ !row.is_primary && !confirming && /* @__PURE__ */ jsx(
979
+ "button",
980
+ {
981
+ type: "button",
982
+ onClick: () => setConfirming(true),
983
+ style: dangerBtnStyle(false),
984
+ children: "unlink"
985
+ }
986
+ ),
987
+ !row.is_primary && confirming && /* @__PURE__ */ jsxs(Fragment, { children: [
988
+ /* @__PURE__ */ jsx(
989
+ "button",
990
+ {
991
+ type: "button",
992
+ onClick: () => void unlink(),
993
+ disabled: busy,
994
+ style: dangerBtnStyle(busy),
995
+ children: busy ? "unlinking\u2026" : "confirm unlink"
996
+ }
997
+ ),
998
+ /* @__PURE__ */ jsx(
999
+ "button",
1000
+ {
1001
+ type: "button",
1002
+ onClick: () => setConfirming(false),
1003
+ disabled: busy,
1004
+ style: ghostBtnStyle(busy),
1005
+ children: "cancel"
1006
+ }
1007
+ )
1008
+ ] })
1009
+ ] })
1010
+ ] });
1011
+ }
1012
+ function ConsolidationPanel({ entries }) {
1013
+ return /* @__PURE__ */ jsxs("div", { style: panelStyle("success"), "data-oc-li-consolidated": "", children: [
1014
+ /* @__PURE__ */ jsxs(SectionLabel, { tone: "success", children: [
1015
+ "\xA7 consolidated accounts \xB7 ",
1016
+ entries.length
1017
+ ] }),
1018
+ /* @__PURE__ */ jsxs("p", { style: bodyStyle, children: [
1019
+ entries.length === 1 ? "A previous OC account" : "Previous OC accounts",
1020
+ " you signed up with, folded into this one under dual proof of control. Historical activity is unioned into this account automatically;",
1021
+ " ",
1022
+ entries.length === 1 ? "it" : "they",
1023
+ " can no longer sign in."
1024
+ ] }),
1025
+ /* @__PURE__ */ jsx("ul", { style: { listStyle: "none", margin: "12px 0 0", padding: 0 }, children: entries.map((e) => /* @__PURE__ */ jsxs(
1026
+ "li",
1027
+ {
1028
+ style: {
1029
+ display: "flex",
1030
+ flexWrap: "wrap",
1031
+ gap: 8,
1032
+ padding: "8px 0",
1033
+ borderTop: `1px solid ${V.border}`,
1034
+ fontFamily: MONO,
1035
+ fontSize: 11
1036
+ },
1037
+ children: [
1038
+ /* @__PURE__ */ jsx("code", { style: { color: V.foreground, wordBreak: "break-all" }, children: e.did_oc }),
1039
+ /* @__PURE__ */ jsxs("span", { style: { ...hintStyle, marginLeft: "auto" }, children: [
1040
+ "created ",
1041
+ new Date(e.created_at).toLocaleDateString()
1042
+ ] })
1043
+ ]
1044
+ },
1045
+ e.did_oc
1046
+ )) })
1047
+ ] });
1048
+ }
1049
+ function EmailLinkForm({
1050
+ authOrigin,
1051
+ onDone,
1052
+ onCancel
1053
+ }) {
1054
+ const [step, setStep] = React3.useState("email");
1055
+ const [email, setEmail] = React3.useState("");
1056
+ const [code, setCode] = React3.useState("");
1057
+ const [token, setToken] = React3.useState("");
1058
+ const [busy, setBusy] = React3.useState(false);
1059
+ const [err, setErr] = React3.useState(null);
1060
+ const [transfer, setTransfer] = React3.useState(false);
1061
+ async function start() {
1062
+ setBusy(true);
1063
+ setErr(null);
1064
+ try {
1065
+ const r = await fetch(`${authOrigin}/api/auth/email-otp/start`, {
1066
+ method: "POST",
1067
+ credentials: "include",
1068
+ headers: { "Content-Type": "application/json" },
1069
+ body: JSON.stringify({ email: email.trim().toLowerCase() })
1070
+ });
1071
+ const j = await r.json();
1072
+ if (!r.ok || !j.token) throw new Error(j.reason ?? `http_${r.status}`);
1073
+ setToken(j.token);
1074
+ setStep("code");
1075
+ } catch (e) {
1076
+ setErr(e instanceof Error ? e.message : "could not send code");
1077
+ } finally {
1078
+ setBusy(false);
1079
+ }
1080
+ }
1081
+ async function verify(confirmTransfer) {
1082
+ setBusy(true);
1083
+ setErr(null);
1084
+ try {
1085
+ const r = await fetch(`${authOrigin}/api/auth/link/email`, {
1086
+ method: "POST",
1087
+ credentials: "include",
1088
+ headers: { "Content-Type": "application/json" },
1089
+ body: JSON.stringify({
1090
+ email: email.trim().toLowerCase(),
1091
+ code: code.trim(),
1092
+ token,
1093
+ ...confirmTransfer ? { confirm_transfer: true } : {}
1094
+ })
1095
+ });
1096
+ const j = await r.json();
1097
+ if (r.status === 409 && j.reason === "email_linked_elsewhere") {
1098
+ setTransfer(true);
1099
+ setBusy(false);
1100
+ return;
1101
+ }
1102
+ if (!r.ok || !j.ok) throw new Error(j.reason ?? `http_${r.status}`);
1103
+ onDone();
1104
+ } catch (e) {
1105
+ setErr(e instanceof Error ? e.message : "link failed");
1106
+ setBusy(false);
1107
+ }
1108
+ }
1109
+ return /* @__PURE__ */ jsxs("div", { style: panelStyle("accent"), "data-oc-li-form": "email", children: [
1110
+ /* @__PURE__ */ jsx(SectionLabel, { children: "\xA7 link an email" }),
1111
+ step === "email" ? /* @__PURE__ */ jsxs(Fragment, { children: [
1112
+ /* @__PURE__ */ jsx(
1113
+ "input",
1114
+ {
1115
+ type: "email",
1116
+ autoComplete: "email",
1117
+ value: email,
1118
+ onChange: (e) => setEmail(e.target.value),
1119
+ placeholder: "you@example.com",
1120
+ style: inputStyle
1121
+ }
1122
+ ),
1123
+ err && /* @__PURE__ */ jsx(ErrorLine, { children: err }),
1124
+ /* @__PURE__ */ jsxs(FormButtons, { children: [
1125
+ /* @__PURE__ */ jsx(
1126
+ "button",
1127
+ {
1128
+ type: "button",
1129
+ onClick: () => void start(),
1130
+ disabled: busy || email.trim().length < 5,
1131
+ style: primaryBtnStyle(busy || email.trim().length < 5),
1132
+ children: busy ? "sending\u2026" : "send code"
1133
+ }
1134
+ ),
1135
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: onCancel, style: ghostBtnStyle(busy), children: "cancel" })
1136
+ ] })
1137
+ ] }) : transfer ? /* @__PURE__ */ jsx(
1138
+ TransferConfirm,
1139
+ {
1140
+ what: "email",
1141
+ busy,
1142
+ onConfirm: () => void verify(true),
1143
+ onCancel,
1144
+ err
1145
+ }
1146
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
1147
+ /* @__PURE__ */ jsxs("p", { style: bodyStyle, children: [
1148
+ "Enter the 6-digit code sent to ",
1149
+ email,
1150
+ "."
1151
+ ] }),
1152
+ /* @__PURE__ */ jsx(
1153
+ "input",
1154
+ {
1155
+ type: "text",
1156
+ inputMode: "numeric",
1157
+ autoComplete: "one-time-code",
1158
+ maxLength: 6,
1159
+ value: code,
1160
+ onChange: (e) => setCode(e.target.value.replace(/\D/g, "").slice(0, 6)),
1161
+ placeholder: "123456",
1162
+ style: { ...inputStyle, letterSpacing: "0.3em" }
1163
+ }
1164
+ ),
1165
+ err && /* @__PURE__ */ jsx(ErrorLine, { children: err }),
1166
+ /* @__PURE__ */ jsxs(FormButtons, { children: [
1167
+ /* @__PURE__ */ jsx(
1168
+ "button",
1169
+ {
1170
+ type: "button",
1171
+ onClick: () => void verify(false),
1172
+ disabled: busy || code.length !== 6,
1173
+ style: primaryBtnStyle(busy || code.length !== 6),
1174
+ children: busy ? "linking\u2026" : "verify + link"
1175
+ }
1176
+ ),
1177
+ /* @__PURE__ */ jsx(
1178
+ "button",
1179
+ {
1180
+ type: "button",
1181
+ onClick: () => setStep("email"),
1182
+ disabled: busy,
1183
+ style: ghostBtnStyle(busy),
1184
+ children: "back"
1185
+ }
1186
+ )
1187
+ ] })
1188
+ ] })
1189
+ ] });
1190
+ }
1191
+ function BtcLinkForm({
1192
+ authOrigin,
1193
+ didOc,
1194
+ onDone,
1195
+ onCancel
1196
+ }) {
1197
+ const [step, setStep] = React3.useState("request");
1198
+ const [addr, setAddr] = React3.useState("");
1199
+ const [challenge, setChallenge] = React3.useState("");
1200
+ const [busy, setBusy] = React3.useState(false);
1201
+ const [err, setErr] = React3.useState(null);
1202
+ const [pendingSig, setPendingSig] = React3.useState(null);
1203
+ async function getChallenge() {
1204
+ if (!didOc) {
1205
+ setErr("session not loaded yet \xB7 refresh and retry");
1206
+ return;
1207
+ }
1208
+ setBusy(true);
1209
+ setErr(null);
1210
+ try {
1211
+ const r = await fetch(
1212
+ `${authOrigin}/api/challenge?addr=${encodeURIComponent(addr)}&purpose=link&audience=${encodeURIComponent(didOc)}`
1213
+ );
1214
+ const j = await r.json();
1215
+ if (!r.ok || !j.message) throw new Error(j.reason ?? `http_${r.status}`);
1216
+ setChallenge(j.message);
1217
+ setStep("sign");
1218
+ } catch (e) {
1219
+ setErr(e instanceof Error ? e.message : "challenge failed");
1220
+ } finally {
1221
+ setBusy(false);
1222
+ }
1223
+ }
1224
+ async function postLink(sig, confirmTransfer) {
1225
+ setBusy(true);
1226
+ setErr(null);
1227
+ try {
1228
+ const r = await fetch(`${authOrigin}/api/auth/link/btc`, {
1229
+ method: "POST",
1230
+ credentials: "include",
1231
+ headers: { "Content-Type": "application/json" },
1232
+ body: JSON.stringify({
1233
+ message: challenge,
1234
+ signature: sig,
1235
+ ...confirmTransfer ? { confirm_transfer: true } : {}
1236
+ })
1237
+ });
1238
+ const j = await r.json();
1239
+ if (r.status === 409 && j.reason === "address_linked_elsewhere") {
1240
+ setPendingSig(sig);
1241
+ setBusy(false);
1242
+ return;
1243
+ }
1244
+ if (!r.ok || !j.ok) throw new Error(j.reason ?? `http_${r.status}`);
1245
+ onDone();
1246
+ } catch (e) {
1247
+ setErr(e instanceof Error ? e.message : "link failed");
1248
+ setBusy(false);
1249
+ }
1250
+ }
1251
+ return /* @__PURE__ */ jsxs("div", { style: panelStyle("accent"), "data-oc-li-form": "btc", children: [
1252
+ /* @__PURE__ */ jsx(SectionLabel, { children: "\xA7 link a Bitcoin address" }),
1253
+ step === "request" ? /* @__PURE__ */ jsxs(Fragment, { children: [
1254
+ /* @__PURE__ */ jsx("p", { style: bodyStyle, children: "Enter a Bitcoin address you control. We generate a challenge and your wallet signs it (BIP-322)." }),
1255
+ /* @__PURE__ */ jsx(
1256
+ "input",
1257
+ {
1258
+ type: "text",
1259
+ value: addr,
1260
+ onChange: (e) => setAddr(e.target.value.trim()),
1261
+ placeholder: "bc1q\u2026",
1262
+ autoCapitalize: "none",
1263
+ spellCheck: false,
1264
+ style: { ...inputStyle, fontFamily: MONO }
1265
+ }
1266
+ ),
1267
+ err && /* @__PURE__ */ jsx(ErrorLine, { children: err }),
1268
+ /* @__PURE__ */ jsxs(FormButtons, { children: [
1269
+ /* @__PURE__ */ jsx(
1270
+ "button",
1271
+ {
1272
+ type: "button",
1273
+ onClick: () => void getChallenge(),
1274
+ disabled: busy || addr.length < 14,
1275
+ style: primaryBtnStyle(busy || addr.length < 14),
1276
+ children: busy ? "requesting\u2026" : "get challenge"
1277
+ }
1278
+ ),
1279
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: onCancel, style: ghostBtnStyle(busy), children: "cancel" })
1280
+ ] })
1281
+ ] }) : pendingSig ? /* @__PURE__ */ jsx(
1282
+ TransferConfirm,
1283
+ {
1284
+ what: "bitcoin address",
1285
+ busy,
1286
+ onConfirm: () => void postLink(pendingSig, true),
1287
+ onCancel,
1288
+ err
1289
+ }
1290
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
1291
+ /* @__PURE__ */ jsxs("p", { style: bodyStyle, children: [
1292
+ "Sign the challenge with the wallet that controls",
1293
+ " ",
1294
+ /* @__PURE__ */ jsx("span", { style: { fontFamily: MONO, wordBreak: "break-all" }, children: addr }),
1295
+ "."
1296
+ ] }),
1297
+ /* @__PURE__ */ jsx(
1298
+ LazyWalletButton,
1299
+ {
1300
+ address: addr,
1301
+ message: challenge,
1302
+ showManual: true,
1303
+ layout: "list",
1304
+ heading: null,
1305
+ onSigned: (sig) => void postLink(sig, false),
1306
+ onError: (e) => setErr(e?.message ?? "wallet rejected")
1307
+ }
1308
+ ),
1309
+ err && /* @__PURE__ */ jsx(ErrorLine, { children: err }),
1310
+ /* @__PURE__ */ jsx(FormButtons, { children: /* @__PURE__ */ jsx(
1311
+ "button",
1312
+ {
1313
+ type: "button",
1314
+ onClick: () => {
1315
+ setStep("request");
1316
+ setErr(null);
1317
+ },
1318
+ disabled: busy,
1319
+ style: ghostBtnStyle(busy),
1320
+ children: "back"
1321
+ }
1322
+ ) })
1323
+ ] })
1324
+ ] });
1325
+ }
1326
+ function TransferConfirm({
1327
+ what,
1328
+ busy,
1329
+ onConfirm,
1330
+ onCancel,
1331
+ err
1332
+ }) {
1333
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1334
+ /* @__PURE__ */ jsxs("p", { style: bodyStyle, children: [
1335
+ "This ",
1336
+ what,
1337
+ " is already linked to another OrangeCheck account. You've just proven control of it \u2014 you can transfer the link to this account under dual proof of control. The other account loses this sign-in path; its history stays verifiable. Continue?"
1338
+ ] }),
1339
+ err && /* @__PURE__ */ jsx(ErrorLine, { children: err }),
1340
+ /* @__PURE__ */ jsxs(FormButtons, { children: [
1341
+ /* @__PURE__ */ jsx(
1342
+ "button",
1343
+ {
1344
+ type: "button",
1345
+ onClick: onConfirm,
1346
+ disabled: busy,
1347
+ style: dangerBtnStyle(busy),
1348
+ children: busy ? "transferring\u2026" : "transfer link here"
1349
+ }
1350
+ ),
1351
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: onCancel, disabled: busy, style: ghostBtnStyle(busy), children: "cancel" })
1352
+ ] })
1353
+ ] });
1354
+ }
1355
+ function LazyWalletButton(props) {
1356
+ const [Comp, setComp] = React3.useState(null);
1357
+ const [failed, setFailed] = React3.useState(false);
1358
+ React3.useEffect(() => {
1359
+ let alive = true;
1360
+ import('@orangecheck/wallet-adapter/react').then((m) => {
1361
+ if (alive) {
1362
+ setComp(
1363
+ () => m.OcWalletButton
1364
+ );
1365
+ }
1366
+ }).catch(() => {
1367
+ if (alive) setFailed(true);
1368
+ });
1369
+ return () => {
1370
+ alive = false;
1371
+ };
1372
+ }, []);
1373
+ if (failed) {
1374
+ return /* @__PURE__ */ jsx(ErrorLine, { children: "@orangecheck/wallet-adapter not installed \xB7 add it to your site to link a wallet" });
1375
+ }
1376
+ if (!Comp) return /* @__PURE__ */ jsxs("span", { style: hintStyle, children: [
1377
+ "> ",
1378
+ "loading wallet options\u2026"
1379
+ ] });
1380
+ return /* @__PURE__ */ jsx(Comp, { ...props });
1381
+ }
1382
+ function mask(value, kind) {
1383
+ if (kind === "email") {
1384
+ const [user, host] = value.split("@");
1385
+ if (!user || !host) return "\u2022\u2022\u2022\u2022";
1386
+ const m = user.length <= 2 ? "\u2022".repeat(user.length) : user[0] + "\u2022".repeat(user.length - 2) + user.slice(-1);
1387
+ return `${m}@${host}`;
1388
+ }
1389
+ if (value.length <= 12) return "\u2022".repeat(value.length);
1390
+ return `${value.slice(0, 6)}\u2026${value.slice(-4)}`;
1391
+ }
1392
+ function SectionLabel({
1393
+ children,
1394
+ tone = "primary"
1395
+ }) {
1396
+ const color = tone === "success" ? V.success : tone === "warn" ? V.warn : V.primary;
1397
+ return /* @__PURE__ */ jsx(
1398
+ "div",
1399
+ {
1400
+ style: {
1401
+ color,
1402
+ fontFamily: MONO,
1403
+ fontSize: 10,
1404
+ letterSpacing: "0.18em",
1405
+ textTransform: "uppercase",
1406
+ marginBottom: 8
1407
+ },
1408
+ children
1409
+ }
1410
+ );
1411
+ }
1412
+ function ErrorLine({ children }) {
1413
+ return /* @__PURE__ */ jsx(
1414
+ "p",
1415
+ {
1416
+ role: "alert",
1417
+ style: {
1418
+ margin: "8px 0 0",
1419
+ color: V.destructive,
1420
+ fontFamily: MONO,
1421
+ fontSize: 11
1422
+ },
1423
+ children
1424
+ }
1425
+ );
1426
+ }
1427
+ function FormButtons({ children }) {
1428
+ return /* @__PURE__ */ jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 8, marginTop: 12 }, children });
1429
+ }
1430
+ var MONO = "ui-monospace, SFMono-Regular, monospace";
1431
+ var V = {
1432
+ primary: "var(--primary, #f97316)",
1433
+ foreground: "var(--foreground, #fafafa)",
1434
+ muted: "var(--muted-foreground, #a1a1aa)",
1435
+ background: "var(--background, #0a0a0a)",
1436
+ card: "var(--card, #111113)",
1437
+ border: "var(--border, #27272a)",
1438
+ destructive: "var(--destructive, #ef4444)",
1439
+ success: "var(--success, #22c55e)",
1440
+ warn: "var(--warning, #eab308)"
1441
+ };
1442
+ function panelStyle(tone = "default") {
1443
+ const base = {
1444
+ background: V.card,
1445
+ border: `1px solid ${V.border}`,
1446
+ padding: "1.25rem"
1447
+ };
1448
+ if (tone === "plain") return { ...base, padding: 0 };
1449
+ if (tone === "accent") return { ...base, borderColor: V.primary, background: "var(--card, #111113)" };
1450
+ if (tone === "success") return { ...base, borderColor: V.success };
1451
+ if (tone === "warn") return { ...base, borderColor: V.warn };
1452
+ return base;
1453
+ }
1454
+ var bodyStyle = {
1455
+ color: V.muted,
1456
+ fontSize: 13,
1457
+ lineHeight: 1.55,
1458
+ margin: "0 0 12px"
1459
+ };
1460
+ var hintStyle = {
1461
+ color: V.muted,
1462
+ fontFamily: MONO,
1463
+ fontSize: 10.5,
1464
+ opacity: 0.8
1465
+ };
1466
+ var codeStyle = {
1467
+ fontFamily: MONO,
1468
+ color: V.foreground
1469
+ };
1470
+ var rowStyle = {
1471
+ display: "flex",
1472
+ flexWrap: "wrap",
1473
+ alignItems: "center",
1474
+ justifyContent: "space-between",
1475
+ gap: 12,
1476
+ padding: "1rem"
1477
+ };
1478
+ var rowLabelStyle = {
1479
+ color: V.foreground,
1480
+ fontFamily: MONO,
1481
+ fontSize: 10.5,
1482
+ letterSpacing: "0.12em",
1483
+ textTransform: "uppercase",
1484
+ opacity: 0.8
1485
+ };
1486
+ var inputStyle = {
1487
+ width: "100%",
1488
+ padding: "0.55rem 0.75rem",
1489
+ background: V.background,
1490
+ color: V.foreground,
1491
+ border: `1px solid ${V.border}`,
1492
+ borderRadius: 6,
1493
+ fontFamily: MONO,
1494
+ // 16px — never lower · suppresses iOS Safari focus auto-zoom.
1495
+ fontSize: 16,
1496
+ outline: "none",
1497
+ boxSizing: "border-box"
1498
+ };
1499
+ function btnBase(disabled) {
1500
+ return {
1501
+ padding: "0.5rem 0.875rem",
1502
+ borderRadius: 6,
1503
+ fontFamily: MONO,
1504
+ fontSize: 10.5,
1505
+ letterSpacing: "0.12em",
1506
+ textTransform: "uppercase",
1507
+ cursor: disabled ? "not-allowed" : "pointer",
1508
+ opacity: disabled ? 0.5 : 1
1509
+ };
1510
+ }
1511
+ function primaryBtnStyle(disabled) {
1512
+ return {
1513
+ ...btnBase(disabled),
1514
+ background: V.primary,
1515
+ color: "var(--primary-foreground, #0a0a0a)",
1516
+ border: `1px solid ${V.primary}`,
1517
+ fontWeight: 600
1518
+ };
1519
+ }
1520
+ function ghostBtnStyle(disabled) {
1521
+ return {
1522
+ ...btnBase(disabled),
1523
+ background: V.background,
1524
+ color: V.foreground,
1525
+ border: `1px solid ${V.border}`
1526
+ };
1527
+ }
1528
+ function dangerBtnStyle(disabled) {
1529
+ return {
1530
+ ...btnBase(disabled),
1531
+ background: "transparent",
1532
+ color: V.destructive,
1533
+ border: `1px solid ${V.destructive}`
1534
+ };
1535
+ }
1536
+ var primaryChipStyle = {
1537
+ ...btnBase(false),
1538
+ background: "transparent",
1539
+ color: V.primary,
1540
+ border: `1px solid ${V.primary}`
1541
+ };
751
1542
  function safeReturnTo(input) {
752
1543
  const candidate = input ?? "/";
753
1544
  if (typeof candidate !== "string") return "/";
@@ -763,6 +1554,7 @@ function OcSignIn({
763
1554
  returnTo,
764
1555
  onSuccess,
765
1556
  resolveReturnTo,
1557
+ linkPrompt,
766
1558
  authOrigin = "https://ochk.io",
767
1559
  initialPath = "wallet",
768
1560
  paths,
@@ -771,24 +1563,34 @@ function OcSignIn({
771
1563
  const walletEnabled = paths?.wallet ?? true;
772
1564
  const emailEnabled = paths?.email ?? true;
773
1565
  const safeReturn = safeReturnTo(returnTo);
774
- const [path, setPath] = React4.useState(initialPath);
775
- const handleSuccess = React4.useCallback(
776
- async (account, token) => {
777
- if (onSuccess) {
778
- onSuccess(account, token);
779
- return;
780
- }
1566
+ const [path, setPath] = React3.useState(initialPath);
1567
+ const [signedIn, setSignedIn] = React3.useState(null);
1568
+ const navigate = React3.useCallback(
1569
+ async (account) => {
781
1570
  if (resolveReturnTo) {
782
1571
  try {
783
- const target = await resolveReturnTo(account);
784
- hardNavigate(safeReturnTo(target));
1572
+ hardNavigate(safeReturnTo(await resolveReturnTo(account)));
785
1573
  return;
786
1574
  } catch {
787
1575
  }
788
1576
  }
789
1577
  hardNavigate(safeReturn);
790
1578
  },
791
- [onSuccess, resolveReturnTo, safeReturn]
1579
+ [resolveReturnTo, safeReturn]
1580
+ );
1581
+ const handleSuccess = React3.useCallback(
1582
+ async (account, token) => {
1583
+ if (onSuccess) {
1584
+ onSuccess(account, token);
1585
+ return;
1586
+ }
1587
+ if (linkPrompt) {
1588
+ setSignedIn(account);
1589
+ return;
1590
+ }
1591
+ await navigate(account);
1592
+ },
1593
+ [onSuccess, linkPrompt, navigate]
792
1594
  );
793
1595
  if (!walletEnabled && !emailEnabled) {
794
1596
  return /* @__PURE__ */ jsxs(
@@ -815,6 +1617,15 @@ function OcSignIn({
815
1617
  }
816
1618
  );
817
1619
  }
1620
+ if (signedIn) {
1621
+ return /* @__PURE__ */ jsx("div", { className, "data-oc-signin": "", children: /* @__PURE__ */ jsx(
1622
+ LinkPromptStep,
1623
+ {
1624
+ authOrigin,
1625
+ onContinue: () => void navigate(signedIn)
1626
+ }
1627
+ ) });
1628
+ }
818
1629
  const showWallet = walletEnabled && (path === "wallet" || !emailEnabled);
819
1630
  const showEmail = emailEnabled && (path === "email" || !walletEnabled);
820
1631
  const bothEnabled = walletEnabled && emailEnabled;
@@ -857,6 +1668,25 @@ function OcSignIn({
857
1668
  ] })
858
1669
  ] });
859
1670
  }
1671
+ function LinkPromptStep({
1672
+ authOrigin,
1673
+ onContinue
1674
+ }) {
1675
+ return /* @__PURE__ */ jsxs("div", { "data-oc-signin-linkprompt": "", children: [
1676
+ /* @__PURE__ */ jsx(FlowHeader, { label: "\xA7 signed in \xB7 add a backup", children: "You're in. Linking a second way to sign in \u2014 your email or your Bitcoin wallet \u2014 is also your recovery path: lose one, the other still gets you in. Optional; skip with continue." }),
1677
+ /* @__PURE__ */ jsx(OcLinkedIdentities, { authOrigin }),
1678
+ /* @__PURE__ */ jsx(
1679
+ "button",
1680
+ {
1681
+ type: "button",
1682
+ onClick: onContinue,
1683
+ style: submitStyle(false),
1684
+ "data-oc-signin-continue": "",
1685
+ children: "continue \u2192"
1686
+ }
1687
+ )
1688
+ ] });
1689
+ }
860
1690
  function SigninTab({
861
1691
  active,
862
1692
  onClick,
@@ -888,10 +1718,10 @@ function SigninTab({
888
1718
  );
889
1719
  }
890
1720
  function WalletFlow({ authOrigin, audience, onSuccess }) {
891
- const [address, setAddress] = React4.useState("");
892
- const [error, setError] = React4.useState(null);
893
- const [submitting, setSubmitting] = React4.useState(false);
894
- const [stage, setStage] = React4.useState("enter");
1721
+ const [address, setAddress] = React3.useState("");
1722
+ const [error, setError] = React3.useState(null);
1723
+ const [submitting, setSubmitting] = React3.useState(false);
1724
+ const [stage, setStage] = React3.useState("enter");
895
1725
  async function signIn(e) {
896
1726
  e.preventDefault();
897
1727
  const addr = address.trim();
@@ -970,10 +1800,10 @@ function WalletFlow({ authOrigin, audience, onSuccess }) {
970
1800
  spellCheck: false,
971
1801
  required: true,
972
1802
  "aria-invalid": error ? true : void 0,
973
- style: inputStyle
1803
+ style: inputStyle2
974
1804
  }
975
1805
  ),
976
- error && /* @__PURE__ */ jsx(ErrorLine, { children: error }),
1806
+ error && /* @__PURE__ */ jsx(ErrorLine2, { children: error }),
977
1807
  /* @__PURE__ */ jsx(
978
1808
  "button",
979
1809
  {
@@ -987,13 +1817,13 @@ function WalletFlow({ authOrigin, audience, onSuccess }) {
987
1817
  ] });
988
1818
  }
989
1819
  function EmailFlow({ authOrigin, onSuccess }) {
990
- const [stage, setStage] = React4.useState("enter");
991
- const [email, setEmail] = React4.useState("");
992
- const [emailError, setEmailError] = React4.useState(null);
993
- const [code, setCode] = React4.useState("");
994
- const [codeError, setCodeError] = React4.useState(null);
995
- const [token, setToken] = React4.useState(null);
996
- const [submitting, setSubmitting] = React4.useState(false);
1820
+ const [stage, setStage] = React3.useState("enter");
1821
+ const [email, setEmail] = React3.useState("");
1822
+ const [emailError, setEmailError] = React3.useState(null);
1823
+ const [code, setCode] = React3.useState("");
1824
+ const [codeError, setCodeError] = React3.useState(null);
1825
+ const [token, setToken] = React3.useState(null);
1826
+ const [submitting, setSubmitting] = React3.useState(false);
997
1827
  async function start(e) {
998
1828
  e.preventDefault();
999
1829
  const trimmed = email.trim().toLowerCase();
@@ -1069,10 +1899,10 @@ function EmailFlow({ authOrigin, onSuccess }) {
1069
1899
  placeholder: "you@example.com",
1070
1900
  required: true,
1071
1901
  "aria-invalid": emailError ? true : void 0,
1072
- style: inputStyle
1902
+ style: inputStyle2
1073
1903
  }
1074
1904
  ),
1075
- emailError && /* @__PURE__ */ jsx(ErrorLine, { children: emailError }),
1905
+ emailError && /* @__PURE__ */ jsx(ErrorLine2, { children: emailError }),
1076
1906
  /* @__PURE__ */ jsx("button", { type: "submit", disabled: submitting, style: submitStyle(submitting), children: submitting ? "sending\u2026" : "send one-time code \u2192" }),
1077
1907
  /* @__PURE__ */ jsx(Hint, { children: "OTP delivered by the auth host. Codes expire in 10 minutes and are single-use. Rate-limited 5 starts/hour/IP." })
1078
1908
  ] });
@@ -1101,10 +1931,10 @@ function EmailFlow({ authOrigin, onSuccess }) {
1101
1931
  required: true,
1102
1932
  autoFocus: true,
1103
1933
  "aria-invalid": codeError ? true : void 0,
1104
- style: { ...inputStyle, letterSpacing: "0.4em", fontSize: 16 }
1934
+ style: { ...inputStyle2, letterSpacing: "0.4em", fontSize: 16 }
1105
1935
  }
1106
1936
  ),
1107
- codeError && /* @__PURE__ */ jsx(ErrorLine, { children: codeError }),
1937
+ codeError && /* @__PURE__ */ jsx(ErrorLine2, { children: codeError }),
1108
1938
  /* @__PURE__ */ jsx(
1109
1939
  "button",
1110
1940
  {
@@ -1191,7 +2021,7 @@ function Label({ children }) {
1191
2021
  }
1192
2022
  );
1193
2023
  }
1194
- var inputStyle = {
2024
+ var inputStyle2 = {
1195
2025
  width: "100%",
1196
2026
  padding: "0.55rem 0.75rem",
1197
2027
  background: "var(--background, #0a0a0a)",
@@ -1223,7 +2053,7 @@ function submitStyle(disabled) {
1223
2053
  width: "100%"
1224
2054
  };
1225
2055
  }
1226
- function ErrorLine({ children }) {
2056
+ function ErrorLine2({ children }) {
1227
2057
  return /* @__PURE__ */ jsx(
1228
2058
  "p",
1229
2059
  {
@@ -1270,13 +2100,13 @@ function isAbortError(err) {
1270
2100
  }
1271
2101
  function useWebAuthnRegister(opts) {
1272
2102
  const authOrigin = resolveAuthOrigin(opts);
1273
- const [status, setStatus] = React4.useState("idle");
1274
- const [error, setError] = React4.useState(null);
1275
- const reset = React4.useCallback(() => {
2103
+ const [status, setStatus] = React3.useState("idle");
2104
+ const [error, setError] = React3.useState(null);
2105
+ const reset = React3.useCallback(() => {
1276
2106
  setStatus("idle");
1277
2107
  setError(null);
1278
2108
  }, []);
1279
- const register = React4.useCallback(
2109
+ const register = React3.useCallback(
1280
2110
  async (args) => {
1281
2111
  setError(null);
1282
2112
  setStatus("requesting-options");
@@ -1338,10 +2168,10 @@ function useWebAuthnRegister(opts) {
1338
2168
  function useWebAuthnList(opts) {
1339
2169
  const authOrigin = resolveAuthOrigin(opts);
1340
2170
  const { status: sessionStatus } = useOcSession();
1341
- const [credentials, setCredentials] = React4.useState([]);
1342
- const [status, setStatus] = React4.useState("loading");
1343
- const [error, setError] = React4.useState(null);
1344
- const refetch = React4.useCallback(async () => {
2171
+ const [credentials, setCredentials] = React3.useState([]);
2172
+ const [status, setStatus] = React3.useState("loading");
2173
+ const [error, setError] = React3.useState(null);
2174
+ const refetch = React3.useCallback(async () => {
1345
2175
  setError(null);
1346
2176
  if (sessionStatus !== "authenticated") {
1347
2177
  setCredentials([]);
@@ -1369,10 +2199,10 @@ function useWebAuthnList(opts) {
1369
2199
  setStatus("error");
1370
2200
  }
1371
2201
  }, [authOrigin, sessionStatus]);
1372
- React4.useEffect(() => {
2202
+ React3.useEffect(() => {
1373
2203
  void refetch();
1374
2204
  }, [refetch]);
1375
- const rename = React4.useCallback(
2205
+ const rename = React3.useCallback(
1376
2206
  async (id, label) => {
1377
2207
  try {
1378
2208
  const r = await fetch(`${authOrigin}/api/auth/webauthn/credentials/${id}`, {
@@ -1397,7 +2227,7 @@ function useWebAuthnList(opts) {
1397
2227
  },
1398
2228
  [authOrigin, refetch]
1399
2229
  );
1400
- const remove = React4.useCallback(
2230
+ const remove = React3.useCallback(
1401
2231
  async (id) => {
1402
2232
  try {
1403
2233
  const r = await fetch(`${authOrigin}/api/auth/webauthn/credentials/${id}`, {
@@ -1425,13 +2255,13 @@ function useWebAuthnList(opts) {
1425
2255
  function useStepUpAuth(opts) {
1426
2256
  const authOrigin = resolveAuthOrigin(opts);
1427
2257
  const { refresh } = useOcSession();
1428
- const [status, setStatus] = React4.useState("idle");
1429
- const [error, setError] = React4.useState(null);
1430
- const reset = React4.useCallback(() => {
2258
+ const [status, setStatus] = React3.useState("idle");
2259
+ const [error, setError] = React3.useState(null);
2260
+ const reset = React3.useCallback(() => {
1431
2261
  setStatus("idle");
1432
2262
  setError(null);
1433
2263
  }, []);
1434
- const stepUp = React4.useCallback(
2264
+ const stepUp = React3.useCallback(
1435
2265
  async (args) => {
1436
2266
  setError(null);
1437
2267
  setStatus("requesting-options");
@@ -1516,6 +2346,6 @@ function handleSudoRequired(body, args = {}) {
1516
2346
  return false;
1517
2347
  }
1518
2348
 
1519
- export { DEFAULT_CONFIG, OcAccountChip, OcAccountPill, OcAddressInput, OcSessionProvider, OcSignIn, OcSignInButton, buildSignInUrl, handleSudoRequired, redirectToSudo, useOcAddressSuggestion, useOcSession, useOptionalOcSession, useStepUpAuth, useWebAuthnList, useWebAuthnRegister };
2349
+ export { DEFAULT_CONFIG, OcAccountChip, OcAccountPill, OcAddressInput, OcLinkedIdentities, OcSessionProvider, OcSignIn, OcSignInButton, buildSignInUrl, handleSudoRequired, redirectToSudo, useOcAddressSuggestion, useOcSession, useOptionalOcSession, useStepUpAuth, useWebAuthnList, useWebAuthnRegister };
1520
2350
  //# sourceMappingURL=index.mjs.map
1521
2351
  //# sourceMappingURL=index.mjs.map