@orangecheck/auth-client 2.5.1 → 2.7.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 +24 -2
- package/dist/index.d.ts +24 -2
- package/dist/index.js +1083 -71
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1082 -72
- package/dist/index.mjs.map +1 -1
- package/package.json +11 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var React3 = require('react');
|
|
4
4
|
var jsxRuntime = require('react/jsx-runtime');
|
|
5
5
|
var browser = require('@simplewebauthn/browser');
|
|
6
6
|
|
|
@@ -22,7 +22,7 @@ function _interopNamespace(e) {
|
|
|
22
22
|
return Object.freeze(n);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
var
|
|
25
|
+
var React3__namespace = /*#__PURE__*/_interopNamespace(React3);
|
|
26
26
|
|
|
27
27
|
// src/provider.tsx
|
|
28
28
|
|
|
@@ -43,7 +43,7 @@ function buildSignInUrl(cfg, returnTo) {
|
|
|
43
43
|
u.searchParams.set("return_to", returnTo);
|
|
44
44
|
return u.toString();
|
|
45
45
|
}
|
|
46
|
-
var SessionContext =
|
|
46
|
+
var SessionContext = React3__namespace.createContext(null);
|
|
47
47
|
function normalizeAccount(raw) {
|
|
48
48
|
if (!raw) return null;
|
|
49
49
|
const didOc = raw.did_oc ?? raw.didOc;
|
|
@@ -66,11 +66,11 @@ function OcSessionProvider({
|
|
|
66
66
|
config,
|
|
67
67
|
defaultReturnTo
|
|
68
68
|
}) {
|
|
69
|
-
const cfg =
|
|
70
|
-
const [account, setAccount] =
|
|
71
|
-
const [status, setStatus] =
|
|
72
|
-
const [error, setError] =
|
|
73
|
-
const refresh =
|
|
69
|
+
const cfg = React3__namespace.useMemo(() => resolveConfig(config), [config]);
|
|
70
|
+
const [account, setAccount] = React3__namespace.useState(null);
|
|
71
|
+
const [status, setStatus] = React3__namespace.useState("loading");
|
|
72
|
+
const [error, setError] = React3__namespace.useState(null);
|
|
73
|
+
const refresh = React3__namespace.useCallback(async () => {
|
|
74
74
|
if (typeof window === "undefined") return;
|
|
75
75
|
try {
|
|
76
76
|
const res = await fetch(cfg.mePath, {
|
|
@@ -99,10 +99,10 @@ function OcSessionProvider({
|
|
|
99
99
|
setError(err instanceof Error ? err : new Error(String(err)));
|
|
100
100
|
}
|
|
101
101
|
}, [cfg.mePath]);
|
|
102
|
-
|
|
102
|
+
React3__namespace.useEffect(() => {
|
|
103
103
|
void refresh();
|
|
104
104
|
}, [refresh]);
|
|
105
|
-
const signOut =
|
|
105
|
+
const signOut = React3__namespace.useCallback(async () => {
|
|
106
106
|
try {
|
|
107
107
|
await fetch(`${cfg.authOrigin}${cfg.logoutPath}`, {
|
|
108
108
|
method: "POST",
|
|
@@ -113,7 +113,7 @@ function OcSessionProvider({
|
|
|
113
113
|
setAccount(null);
|
|
114
114
|
setStatus("anonymous");
|
|
115
115
|
}, [cfg.authOrigin, cfg.logoutPath]);
|
|
116
|
-
const value =
|
|
116
|
+
const value = React3__namespace.useMemo(() => {
|
|
117
117
|
const returnTo = defaultReturnTo ?? (typeof window !== "undefined" ? window.location.href : void 0);
|
|
118
118
|
return {
|
|
119
119
|
status,
|
|
@@ -127,7 +127,7 @@ function OcSessionProvider({
|
|
|
127
127
|
return /* @__PURE__ */ jsxRuntime.jsx(SessionContext.Provider, { value, children });
|
|
128
128
|
}
|
|
129
129
|
function useOcSession() {
|
|
130
|
-
const ctx =
|
|
130
|
+
const ctx = React3__namespace.useContext(SessionContext);
|
|
131
131
|
if (!ctx) {
|
|
132
132
|
throw new Error(
|
|
133
133
|
"[@orangecheck/auth-client] useOcSession() must be called inside <OcSessionProvider>"
|
|
@@ -136,7 +136,7 @@ function useOcSession() {
|
|
|
136
136
|
return ctx;
|
|
137
137
|
}
|
|
138
138
|
function useOptionalOcSession() {
|
|
139
|
-
return
|
|
139
|
+
return React3__namespace.useContext(SessionContext);
|
|
140
140
|
}
|
|
141
141
|
function shortenAddress(addr) {
|
|
142
142
|
if (addr.length <= 12) return addr;
|
|
@@ -151,7 +151,7 @@ function isPrefixOf(value, target) {
|
|
|
151
151
|
}
|
|
152
152
|
var listboxIdCounter = 0;
|
|
153
153
|
function useUniqueId(prefix) {
|
|
154
|
-
const [id] =
|
|
154
|
+
const [id] = React3__namespace.useState(() => `${prefix}-${++listboxIdCounter}`);
|
|
155
155
|
return id;
|
|
156
156
|
}
|
|
157
157
|
function OcAccountChip({
|
|
@@ -165,9 +165,9 @@ function OcAccountChip({
|
|
|
165
165
|
}) {
|
|
166
166
|
const { status, account, signInUrl: sessionSignInUrl, signOut } = useOcSession();
|
|
167
167
|
const signInUrl = signInUrlOverride ?? sessionSignInUrl;
|
|
168
|
-
const [open, setOpen] =
|
|
169
|
-
const wrapRef =
|
|
170
|
-
|
|
168
|
+
const [open, setOpen] = React3__namespace.useState(false);
|
|
169
|
+
const wrapRef = React3__namespace.useRef(null);
|
|
170
|
+
React3__namespace.useEffect(() => {
|
|
171
171
|
if (!open) return;
|
|
172
172
|
function handleClickOutside(e) {
|
|
173
173
|
if (wrapRef.current && !wrapRef.current.contains(e.target)) {
|
|
@@ -490,9 +490,9 @@ function useOcAddressSuggestion(options) {
|
|
|
490
490
|
} = options;
|
|
491
491
|
const { status, account } = useOcSession();
|
|
492
492
|
const sessionAddress = status === "authenticated" ? account?.primaryBtc ?? null : null;
|
|
493
|
-
const [open, setOpen] =
|
|
494
|
-
const [highlighted, setHighlighted] =
|
|
495
|
-
const blurTimer =
|
|
493
|
+
const [open, setOpen] = React3__namespace.useState(false);
|
|
494
|
+
const [highlighted, setHighlighted] = React3__namespace.useState(false);
|
|
495
|
+
const blurTimer = React3__namespace.useRef(null);
|
|
496
496
|
const listboxId = useUniqueId("oc-addr-listbox");
|
|
497
497
|
const optionId = `${listboxId}-opt`;
|
|
498
498
|
const valueMatchesSession = sessionAddress != null && value.toLowerCase() === sessionAddress.toLowerCase();
|
|
@@ -504,7 +504,7 @@ function useOcAddressSuggestion(options) {
|
|
|
504
504
|
setOpen(false);
|
|
505
505
|
setHighlighted(false);
|
|
506
506
|
}
|
|
507
|
-
|
|
507
|
+
React3__namespace.useEffect(() => {
|
|
508
508
|
return () => {
|
|
509
509
|
if (blurTimer.current != null) window.clearTimeout(blurTimer.current);
|
|
510
510
|
};
|
|
@@ -607,7 +607,7 @@ function useOcAddressSuggestion(options) {
|
|
|
607
607
|
) : null;
|
|
608
608
|
return { inputProps, popover };
|
|
609
609
|
}
|
|
610
|
-
var OcAddressInput =
|
|
610
|
+
var OcAddressInput = React3__namespace.forwardRef(
|
|
611
611
|
function OcAddressInput2({
|
|
612
612
|
value,
|
|
613
613
|
onValueChange,
|
|
@@ -622,15 +622,15 @@ var OcAddressInput = React4__namespace.forwardRef(
|
|
|
622
622
|
}, forwardedRef) {
|
|
623
623
|
const { status, account } = useOcSession();
|
|
624
624
|
const sessionAddress = status === "authenticated" ? account?.primaryBtc ?? null : null;
|
|
625
|
-
const [open, setOpen] =
|
|
626
|
-
const [highlighted, setHighlighted] =
|
|
627
|
-
const innerRef =
|
|
625
|
+
const [open, setOpen] = React3__namespace.useState(false);
|
|
626
|
+
const [highlighted, setHighlighted] = React3__namespace.useState(false);
|
|
627
|
+
const innerRef = React3__namespace.useRef(null);
|
|
628
628
|
const setRef = (node) => {
|
|
629
629
|
innerRef.current = node;
|
|
630
630
|
if (typeof forwardedRef === "function") forwardedRef(node);
|
|
631
631
|
else if (forwardedRef) forwardedRef.current = node;
|
|
632
632
|
};
|
|
633
|
-
const blurTimer =
|
|
633
|
+
const blurTimer = React3__namespace.useRef(null);
|
|
634
634
|
const listboxId = useUniqueId("oc-addr-listbox");
|
|
635
635
|
const optionId = `${listboxId}-opt`;
|
|
636
636
|
const valueMatchesSession = sessionAddress != null && value.toLowerCase() === sessionAddress.toLowerCase();
|
|
@@ -677,7 +677,7 @@ var OcAddressInput = React4__namespace.forwardRef(
|
|
|
677
677
|
}
|
|
678
678
|
onKeyDown?.(e);
|
|
679
679
|
}
|
|
680
|
-
|
|
680
|
+
React3__namespace.useEffect(() => {
|
|
681
681
|
return () => {
|
|
682
682
|
if (blurTimer.current != null) window.clearTimeout(blurTimer.current);
|
|
683
683
|
};
|
|
@@ -770,6 +770,977 @@ var OcAddressInput = React4__namespace.forwardRef(
|
|
|
770
770
|
);
|
|
771
771
|
}
|
|
772
772
|
);
|
|
773
|
+
var DEFAULT_AUTH_ORIGIN = "https://ochk.io";
|
|
774
|
+
function OcLinkedIdentities({
|
|
775
|
+
authOrigin = DEFAULT_AUTH_ORIGIN,
|
|
776
|
+
className,
|
|
777
|
+
onChange
|
|
778
|
+
}) {
|
|
779
|
+
const [resp, setResp] = React3__namespace.useState(null);
|
|
780
|
+
const [error, setError] = React3__namespace.useState(null);
|
|
781
|
+
const [showEmailLink, setShowEmailLink] = React3__namespace.useState(false);
|
|
782
|
+
const [showBtcLink, setShowBtcLink] = React3__namespace.useState(false);
|
|
783
|
+
const refresh = React3__namespace.useCallback(async () => {
|
|
784
|
+
setError(null);
|
|
785
|
+
try {
|
|
786
|
+
const r = await fetch(`${authOrigin}/api/auth/identities`, {
|
|
787
|
+
credentials: "include"
|
|
788
|
+
});
|
|
789
|
+
if (!r.ok) {
|
|
790
|
+
if (r.status === 401) {
|
|
791
|
+
setError("not signed in");
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
throw new Error(`http_${r.status}`);
|
|
795
|
+
}
|
|
796
|
+
setResp(await r.json());
|
|
797
|
+
} catch (e) {
|
|
798
|
+
setError(e instanceof Error ? e.message : "load failed");
|
|
799
|
+
}
|
|
800
|
+
}, [authOrigin]);
|
|
801
|
+
React3__namespace.useEffect(() => {
|
|
802
|
+
void refresh();
|
|
803
|
+
}, [refresh]);
|
|
804
|
+
const afterChange = React3__namespace.useCallback(() => {
|
|
805
|
+
setShowEmailLink(false);
|
|
806
|
+
setShowBtcLink(false);
|
|
807
|
+
void refresh();
|
|
808
|
+
onChange?.();
|
|
809
|
+
}, [refresh, onChange]);
|
|
810
|
+
if (error) {
|
|
811
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, "data-oc-li": "", style: panelStyle("warn"), role: "alert", children: [
|
|
812
|
+
/* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { tone: "warn", children: "\xA7 identities \xB7 load deferred" }),
|
|
813
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { style: bodyStyle, children: [
|
|
814
|
+
"Couldn't reach the auth host (",
|
|
815
|
+
error,
|
|
816
|
+
"). Linking is temporarily unavailable; your signed-in identity still works as normal."
|
|
817
|
+
] }),
|
|
818
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => void refresh(), style: ghostBtnStyle(false), children: "retry" })
|
|
819
|
+
] });
|
|
820
|
+
}
|
|
821
|
+
if (!resp) {
|
|
822
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className, "data-oc-li": "", style: panelStyle(), children: /* @__PURE__ */ jsxRuntime.jsxs("span", { style: hintStyle, children: [
|
|
823
|
+
"> ",
|
|
824
|
+
"loading\u2026"
|
|
825
|
+
] }) });
|
|
826
|
+
}
|
|
827
|
+
const linked = resp.linked ?? [];
|
|
828
|
+
const emailRows = linked.filter((i) => i.kind === "email");
|
|
829
|
+
const btcRows = linked.filter((i) => i.kind === "btc");
|
|
830
|
+
const hasEmail = emailRows.some((i) => i.value);
|
|
831
|
+
const hasBtc = btcRows.some((i) => i.value);
|
|
832
|
+
const mergedFrom = resp.merged_from ?? [];
|
|
833
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, "data-oc-li": "", style: { display: "grid", gap: 16 }, children: [
|
|
834
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: panelStyle(), children: [
|
|
835
|
+
/* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { children: "\xA7 what this is" }),
|
|
836
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { style: bodyStyle, children: [
|
|
837
|
+
"Your public identity is the opaque ",
|
|
838
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { style: codeStyle, children: "did:oc:" }),
|
|
839
|
+
" 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."
|
|
840
|
+
] })
|
|
841
|
+
] }),
|
|
842
|
+
mergedFrom.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(ConsolidationPanel, { entries: mergedFrom }),
|
|
843
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: panelStyle("plain"), "data-oc-li-rows": "", children: [
|
|
844
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
845
|
+
IdentityGroup,
|
|
846
|
+
{
|
|
847
|
+
kind: "email",
|
|
848
|
+
title: "email",
|
|
849
|
+
rows: emailRows,
|
|
850
|
+
authOrigin,
|
|
851
|
+
onChange: afterChange,
|
|
852
|
+
addCta: hasEmail ? null : "link an email",
|
|
853
|
+
onAdd: () => {
|
|
854
|
+
setShowEmailLink((v) => !v);
|
|
855
|
+
setShowBtcLink(false);
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
),
|
|
859
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { borderTop: `1px solid ${V.border}` } }),
|
|
860
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
861
|
+
IdentityGroup,
|
|
862
|
+
{
|
|
863
|
+
kind: "btc",
|
|
864
|
+
title: "bitcoin address",
|
|
865
|
+
rows: btcRows,
|
|
866
|
+
authOrigin,
|
|
867
|
+
onChange: afterChange,
|
|
868
|
+
addCta: hasBtc ? null : "link a Bitcoin address",
|
|
869
|
+
onAdd: () => {
|
|
870
|
+
setShowBtcLink((v) => !v);
|
|
871
|
+
setShowEmailLink(false);
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
)
|
|
875
|
+
] }),
|
|
876
|
+
showEmailLink && /* @__PURE__ */ jsxRuntime.jsx(
|
|
877
|
+
EmailLinkForm,
|
|
878
|
+
{
|
|
879
|
+
authOrigin,
|
|
880
|
+
onDone: afterChange,
|
|
881
|
+
onCancel: () => setShowEmailLink(false)
|
|
882
|
+
}
|
|
883
|
+
),
|
|
884
|
+
showBtcLink && /* @__PURE__ */ jsxRuntime.jsx(
|
|
885
|
+
BtcLinkForm,
|
|
886
|
+
{
|
|
887
|
+
authOrigin,
|
|
888
|
+
didOc: resp.did_oc ?? null,
|
|
889
|
+
onDone: afterChange,
|
|
890
|
+
onCancel: () => setShowBtcLink(false)
|
|
891
|
+
}
|
|
892
|
+
),
|
|
893
|
+
hasBtc && resp.did_oc && /* @__PURE__ */ jsxRuntime.jsx(OcIdentityBond, { didOc: resp.did_oc, btc: btcRows.find((i) => !!i.value).value })
|
|
894
|
+
] });
|
|
895
|
+
}
|
|
896
|
+
function IdentityGroup({
|
|
897
|
+
kind,
|
|
898
|
+
title,
|
|
899
|
+
rows,
|
|
900
|
+
authOrigin,
|
|
901
|
+
onChange,
|
|
902
|
+
addCta,
|
|
903
|
+
onAdd
|
|
904
|
+
}) {
|
|
905
|
+
if (rows.length === 0) {
|
|
906
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: rowStyle, children: [
|
|
907
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
908
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: rowLabelStyle, children: [
|
|
909
|
+
"\xA7 ",
|
|
910
|
+
title
|
|
911
|
+
] }),
|
|
912
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { ...bodyStyle, marginTop: 4 }, children: "none linked" })
|
|
913
|
+
] }),
|
|
914
|
+
addCta && /* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", onClick: onAdd, style: primaryChipStyle, children: [
|
|
915
|
+
"+ ",
|
|
916
|
+
addCta
|
|
917
|
+
] })
|
|
918
|
+
] });
|
|
919
|
+
}
|
|
920
|
+
return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: rows.map((r) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
921
|
+
IdentityRow,
|
|
922
|
+
{
|
|
923
|
+
row: r,
|
|
924
|
+
kind,
|
|
925
|
+
authOrigin,
|
|
926
|
+
onChange
|
|
927
|
+
},
|
|
928
|
+
r.id
|
|
929
|
+
)) });
|
|
930
|
+
}
|
|
931
|
+
function IdentityRow({
|
|
932
|
+
row,
|
|
933
|
+
kind,
|
|
934
|
+
authOrigin,
|
|
935
|
+
onChange
|
|
936
|
+
}) {
|
|
937
|
+
const [shown, setShown] = React3__namespace.useState(false);
|
|
938
|
+
const [confirming, setConfirming] = React3__namespace.useState(false);
|
|
939
|
+
const [busy, setBusy] = React3__namespace.useState(false);
|
|
940
|
+
const [err, setErr] = React3__namespace.useState(null);
|
|
941
|
+
const masked = row.value ? mask(row.value, kind) : "\u2014 pending recovery \u2014";
|
|
942
|
+
const display = shown && row.value ? row.value : masked;
|
|
943
|
+
async function unlink() {
|
|
944
|
+
if (row.is_primary) return;
|
|
945
|
+
setBusy(true);
|
|
946
|
+
setErr(null);
|
|
947
|
+
try {
|
|
948
|
+
const r = await fetch(
|
|
949
|
+
`${authOrigin}/api/auth/identities?id=${encodeURIComponent(row.id)}`,
|
|
950
|
+
{ method: "DELETE", credentials: "include" }
|
|
951
|
+
);
|
|
952
|
+
if (!r.ok) {
|
|
953
|
+
const j = await r.json().catch(() => ({}));
|
|
954
|
+
throw new Error(j.reason ?? `http_${r.status}`);
|
|
955
|
+
}
|
|
956
|
+
onChange();
|
|
957
|
+
} catch (e) {
|
|
958
|
+
setErr(e instanceof Error ? e.message : "unlink failed");
|
|
959
|
+
setBusy(false);
|
|
960
|
+
setConfirming(false);
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: rowStyle, "data-oc-li-row": kind, children: [
|
|
964
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { minWidth: 0, flex: 1 }, children: [
|
|
965
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexWrap: "wrap", gap: "4px 12px" }, children: [
|
|
966
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: rowLabelStyle, children: [
|
|
967
|
+
"\xA7 ",
|
|
968
|
+
kind === "email" ? "email" : "bitcoin address"
|
|
969
|
+
] }),
|
|
970
|
+
row.is_primary && /* @__PURE__ */ jsxRuntime.jsx("span", { style: { ...rowLabelStyle, color: V.primary }, children: "primary \xB7 signup-time" })
|
|
971
|
+
] }),
|
|
972
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
973
|
+
"div",
|
|
974
|
+
{
|
|
975
|
+
style: {
|
|
976
|
+
marginTop: 4,
|
|
977
|
+
color: V.foreground,
|
|
978
|
+
fontFamily: MONO,
|
|
979
|
+
fontSize: 12,
|
|
980
|
+
wordBreak: "break-all"
|
|
981
|
+
},
|
|
982
|
+
children: display
|
|
983
|
+
}
|
|
984
|
+
),
|
|
985
|
+
row.verified_at && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...hintStyle, marginTop: 4 }, children: [
|
|
986
|
+
"verified \xB7 ",
|
|
987
|
+
new Date(row.verified_at).toUTCString()
|
|
988
|
+
] }),
|
|
989
|
+
err && /* @__PURE__ */ jsxRuntime.jsx(ErrorLine, { children: err })
|
|
990
|
+
] }),
|
|
991
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexShrink: 0, gap: 8, flexWrap: "wrap" }, children: [
|
|
992
|
+
row.value && /* @__PURE__ */ jsxRuntime.jsx(
|
|
993
|
+
"button",
|
|
994
|
+
{
|
|
995
|
+
type: "button",
|
|
996
|
+
onClick: () => setShown((s) => !s),
|
|
997
|
+
style: ghostBtnStyle(false),
|
|
998
|
+
children: shown ? "hide" : "show"
|
|
999
|
+
}
|
|
1000
|
+
),
|
|
1001
|
+
!row.is_primary && !confirming && /* @__PURE__ */ jsxRuntime.jsx(
|
|
1002
|
+
"button",
|
|
1003
|
+
{
|
|
1004
|
+
type: "button",
|
|
1005
|
+
onClick: () => setConfirming(true),
|
|
1006
|
+
style: dangerBtnStyle(false),
|
|
1007
|
+
children: "unlink"
|
|
1008
|
+
}
|
|
1009
|
+
),
|
|
1010
|
+
!row.is_primary && confirming && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1011
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1012
|
+
"button",
|
|
1013
|
+
{
|
|
1014
|
+
type: "button",
|
|
1015
|
+
onClick: () => void unlink(),
|
|
1016
|
+
disabled: busy,
|
|
1017
|
+
style: dangerBtnStyle(busy),
|
|
1018
|
+
children: busy ? "unlinking\u2026" : "confirm unlink"
|
|
1019
|
+
}
|
|
1020
|
+
),
|
|
1021
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1022
|
+
"button",
|
|
1023
|
+
{
|
|
1024
|
+
type: "button",
|
|
1025
|
+
onClick: () => setConfirming(false),
|
|
1026
|
+
disabled: busy,
|
|
1027
|
+
style: ghostBtnStyle(busy),
|
|
1028
|
+
children: "cancel"
|
|
1029
|
+
}
|
|
1030
|
+
)
|
|
1031
|
+
] })
|
|
1032
|
+
] })
|
|
1033
|
+
] });
|
|
1034
|
+
}
|
|
1035
|
+
function ConsolidationPanel({ entries }) {
|
|
1036
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: panelStyle("success"), "data-oc-li-consolidated": "", children: [
|
|
1037
|
+
/* @__PURE__ */ jsxRuntime.jsxs(SectionLabel, { tone: "success", children: [
|
|
1038
|
+
"\xA7 consolidated accounts \xB7 ",
|
|
1039
|
+
entries.length
|
|
1040
|
+
] }),
|
|
1041
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { style: bodyStyle, children: [
|
|
1042
|
+
entries.length === 1 ? "A previous OC account" : "Previous OC accounts",
|
|
1043
|
+
" you signed up with, folded into this one under dual proof of control. Historical activity is unioned into this account automatically;",
|
|
1044
|
+
" ",
|
|
1045
|
+
entries.length === 1 ? "it" : "they",
|
|
1046
|
+
" can no longer sign in."
|
|
1047
|
+
] }),
|
|
1048
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { style: { listStyle: "none", margin: "12px 0 0", padding: 0 }, children: entries.map((e) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1049
|
+
"li",
|
|
1050
|
+
{
|
|
1051
|
+
style: {
|
|
1052
|
+
display: "flex",
|
|
1053
|
+
flexWrap: "wrap",
|
|
1054
|
+
gap: 8,
|
|
1055
|
+
padding: "8px 0",
|
|
1056
|
+
borderTop: `1px solid ${V.border}`,
|
|
1057
|
+
fontFamily: MONO,
|
|
1058
|
+
fontSize: 11
|
|
1059
|
+
},
|
|
1060
|
+
children: [
|
|
1061
|
+
/* @__PURE__ */ jsxRuntime.jsx("code", { style: { color: V.foreground, wordBreak: "break-all" }, children: e.did_oc }),
|
|
1062
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: { ...hintStyle, marginLeft: "auto" }, children: [
|
|
1063
|
+
"created ",
|
|
1064
|
+
new Date(e.created_at).toLocaleDateString()
|
|
1065
|
+
] })
|
|
1066
|
+
]
|
|
1067
|
+
},
|
|
1068
|
+
e.did_oc
|
|
1069
|
+
)) })
|
|
1070
|
+
] });
|
|
1071
|
+
}
|
|
1072
|
+
function EmailLinkForm({
|
|
1073
|
+
authOrigin,
|
|
1074
|
+
onDone,
|
|
1075
|
+
onCancel
|
|
1076
|
+
}) {
|
|
1077
|
+
const [step, setStep] = React3__namespace.useState("email");
|
|
1078
|
+
const [email, setEmail] = React3__namespace.useState("");
|
|
1079
|
+
const [code, setCode] = React3__namespace.useState("");
|
|
1080
|
+
const [token, setToken] = React3__namespace.useState("");
|
|
1081
|
+
const [busy, setBusy] = React3__namespace.useState(false);
|
|
1082
|
+
const [err, setErr] = React3__namespace.useState(null);
|
|
1083
|
+
const [transfer, setTransfer] = React3__namespace.useState(false);
|
|
1084
|
+
async function start() {
|
|
1085
|
+
setBusy(true);
|
|
1086
|
+
setErr(null);
|
|
1087
|
+
try {
|
|
1088
|
+
const r = await fetch(`${authOrigin}/api/auth/email-otp/start`, {
|
|
1089
|
+
method: "POST",
|
|
1090
|
+
credentials: "include",
|
|
1091
|
+
headers: { "Content-Type": "application/json" },
|
|
1092
|
+
body: JSON.stringify({ email: email.trim().toLowerCase() })
|
|
1093
|
+
});
|
|
1094
|
+
const j = await r.json();
|
|
1095
|
+
if (!r.ok || !j.token) throw new Error(j.reason ?? `http_${r.status}`);
|
|
1096
|
+
setToken(j.token);
|
|
1097
|
+
setStep("code");
|
|
1098
|
+
} catch (e) {
|
|
1099
|
+
setErr(e instanceof Error ? e.message : "could not send code");
|
|
1100
|
+
} finally {
|
|
1101
|
+
setBusy(false);
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
async function verify(confirmTransfer) {
|
|
1105
|
+
setBusy(true);
|
|
1106
|
+
setErr(null);
|
|
1107
|
+
try {
|
|
1108
|
+
const r = await fetch(`${authOrigin}/api/auth/link/email`, {
|
|
1109
|
+
method: "POST",
|
|
1110
|
+
credentials: "include",
|
|
1111
|
+
headers: { "Content-Type": "application/json" },
|
|
1112
|
+
body: JSON.stringify({
|
|
1113
|
+
email: email.trim().toLowerCase(),
|
|
1114
|
+
code: code.trim(),
|
|
1115
|
+
token,
|
|
1116
|
+
...confirmTransfer ? { confirm_transfer: true } : {}
|
|
1117
|
+
})
|
|
1118
|
+
});
|
|
1119
|
+
const j = await r.json();
|
|
1120
|
+
if (r.status === 409 && j.reason === "email_linked_elsewhere") {
|
|
1121
|
+
setTransfer(true);
|
|
1122
|
+
setBusy(false);
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
if (!r.ok || !j.ok) throw new Error(j.reason ?? `http_${r.status}`);
|
|
1126
|
+
onDone();
|
|
1127
|
+
} catch (e) {
|
|
1128
|
+
setErr(e instanceof Error ? e.message : "link failed");
|
|
1129
|
+
setBusy(false);
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1132
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: panelStyle("accent"), "data-oc-li-form": "email", children: [
|
|
1133
|
+
/* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { children: "\xA7 link an email" }),
|
|
1134
|
+
step === "email" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1135
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1136
|
+
"input",
|
|
1137
|
+
{
|
|
1138
|
+
type: "email",
|
|
1139
|
+
autoComplete: "email",
|
|
1140
|
+
value: email,
|
|
1141
|
+
onChange: (e) => setEmail(e.target.value),
|
|
1142
|
+
placeholder: "you@example.com",
|
|
1143
|
+
style: inputStyle
|
|
1144
|
+
}
|
|
1145
|
+
),
|
|
1146
|
+
err && /* @__PURE__ */ jsxRuntime.jsx(ErrorLine, { children: err }),
|
|
1147
|
+
/* @__PURE__ */ jsxRuntime.jsxs(FormButtons, { children: [
|
|
1148
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1149
|
+
"button",
|
|
1150
|
+
{
|
|
1151
|
+
type: "button",
|
|
1152
|
+
onClick: () => void start(),
|
|
1153
|
+
disabled: busy || email.trim().length < 5,
|
|
1154
|
+
style: primaryBtnStyle(busy || email.trim().length < 5),
|
|
1155
|
+
children: busy ? "sending\u2026" : "send code"
|
|
1156
|
+
}
|
|
1157
|
+
),
|
|
1158
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: onCancel, style: ghostBtnStyle(busy), children: "cancel" })
|
|
1159
|
+
] })
|
|
1160
|
+
] }) : transfer ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1161
|
+
TransferConfirm,
|
|
1162
|
+
{
|
|
1163
|
+
what: "email",
|
|
1164
|
+
busy,
|
|
1165
|
+
onConfirm: () => void verify(true),
|
|
1166
|
+
onCancel,
|
|
1167
|
+
err
|
|
1168
|
+
}
|
|
1169
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1170
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { style: bodyStyle, children: [
|
|
1171
|
+
"Enter the 6-digit code sent to ",
|
|
1172
|
+
email,
|
|
1173
|
+
"."
|
|
1174
|
+
] }),
|
|
1175
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1176
|
+
"input",
|
|
1177
|
+
{
|
|
1178
|
+
type: "text",
|
|
1179
|
+
inputMode: "numeric",
|
|
1180
|
+
autoComplete: "one-time-code",
|
|
1181
|
+
maxLength: 6,
|
|
1182
|
+
value: code,
|
|
1183
|
+
onChange: (e) => setCode(e.target.value.replace(/\D/g, "").slice(0, 6)),
|
|
1184
|
+
placeholder: "123456",
|
|
1185
|
+
style: { ...inputStyle, letterSpacing: "0.3em" }
|
|
1186
|
+
}
|
|
1187
|
+
),
|
|
1188
|
+
err && /* @__PURE__ */ jsxRuntime.jsx(ErrorLine, { children: err }),
|
|
1189
|
+
/* @__PURE__ */ jsxRuntime.jsxs(FormButtons, { children: [
|
|
1190
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1191
|
+
"button",
|
|
1192
|
+
{
|
|
1193
|
+
type: "button",
|
|
1194
|
+
onClick: () => void verify(false),
|
|
1195
|
+
disabled: busy || code.length !== 6,
|
|
1196
|
+
style: primaryBtnStyle(busy || code.length !== 6),
|
|
1197
|
+
children: busy ? "linking\u2026" : "verify + link"
|
|
1198
|
+
}
|
|
1199
|
+
),
|
|
1200
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1201
|
+
"button",
|
|
1202
|
+
{
|
|
1203
|
+
type: "button",
|
|
1204
|
+
onClick: () => setStep("email"),
|
|
1205
|
+
disabled: busy,
|
|
1206
|
+
style: ghostBtnStyle(busy),
|
|
1207
|
+
children: "back"
|
|
1208
|
+
}
|
|
1209
|
+
)
|
|
1210
|
+
] })
|
|
1211
|
+
] })
|
|
1212
|
+
] });
|
|
1213
|
+
}
|
|
1214
|
+
function BtcLinkForm({
|
|
1215
|
+
authOrigin,
|
|
1216
|
+
didOc,
|
|
1217
|
+
onDone,
|
|
1218
|
+
onCancel
|
|
1219
|
+
}) {
|
|
1220
|
+
const [step, setStep] = React3__namespace.useState("request");
|
|
1221
|
+
const [addr, setAddr] = React3__namespace.useState("");
|
|
1222
|
+
const [challenge, setChallenge] = React3__namespace.useState("");
|
|
1223
|
+
const [busy, setBusy] = React3__namespace.useState(false);
|
|
1224
|
+
const [err, setErr] = React3__namespace.useState(null);
|
|
1225
|
+
const [pendingSig, setPendingSig] = React3__namespace.useState(null);
|
|
1226
|
+
async function getChallenge() {
|
|
1227
|
+
if (!didOc) {
|
|
1228
|
+
setErr("session not loaded yet \xB7 refresh and retry");
|
|
1229
|
+
return;
|
|
1230
|
+
}
|
|
1231
|
+
setBusy(true);
|
|
1232
|
+
setErr(null);
|
|
1233
|
+
try {
|
|
1234
|
+
const r = await fetch(
|
|
1235
|
+
`${authOrigin}/api/challenge?addr=${encodeURIComponent(addr)}&purpose=link&audience=${encodeURIComponent(didOc)}`
|
|
1236
|
+
);
|
|
1237
|
+
const j = await r.json();
|
|
1238
|
+
if (!r.ok || !j.message) throw new Error(j.reason ?? `http_${r.status}`);
|
|
1239
|
+
setChallenge(j.message);
|
|
1240
|
+
setStep("sign");
|
|
1241
|
+
} catch (e) {
|
|
1242
|
+
setErr(e instanceof Error ? e.message : "challenge failed");
|
|
1243
|
+
} finally {
|
|
1244
|
+
setBusy(false);
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
async function postLink(sig, confirmTransfer) {
|
|
1248
|
+
setBusy(true);
|
|
1249
|
+
setErr(null);
|
|
1250
|
+
try {
|
|
1251
|
+
const r = await fetch(`${authOrigin}/api/auth/link/btc`, {
|
|
1252
|
+
method: "POST",
|
|
1253
|
+
credentials: "include",
|
|
1254
|
+
headers: { "Content-Type": "application/json" },
|
|
1255
|
+
body: JSON.stringify({
|
|
1256
|
+
message: challenge,
|
|
1257
|
+
signature: sig,
|
|
1258
|
+
...confirmTransfer ? { confirm_transfer: true } : {}
|
|
1259
|
+
})
|
|
1260
|
+
});
|
|
1261
|
+
const j = await r.json();
|
|
1262
|
+
if (r.status === 409 && j.reason === "address_linked_elsewhere") {
|
|
1263
|
+
setPendingSig(sig);
|
|
1264
|
+
setBusy(false);
|
|
1265
|
+
return;
|
|
1266
|
+
}
|
|
1267
|
+
if (!r.ok || !j.ok) throw new Error(j.reason ?? `http_${r.status}`);
|
|
1268
|
+
onDone();
|
|
1269
|
+
} catch (e) {
|
|
1270
|
+
setErr(e instanceof Error ? e.message : "link failed");
|
|
1271
|
+
setBusy(false);
|
|
1272
|
+
}
|
|
1273
|
+
}
|
|
1274
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: panelStyle("accent"), "data-oc-li-form": "btc", children: [
|
|
1275
|
+
/* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { children: "\xA7 link a Bitcoin address" }),
|
|
1276
|
+
step === "request" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1277
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: bodyStyle, children: "Enter a Bitcoin address you control. We generate a challenge and your wallet signs it (BIP-322)." }),
|
|
1278
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1279
|
+
"input",
|
|
1280
|
+
{
|
|
1281
|
+
type: "text",
|
|
1282
|
+
value: addr,
|
|
1283
|
+
onChange: (e) => setAddr(e.target.value.trim()),
|
|
1284
|
+
placeholder: "bc1q\u2026",
|
|
1285
|
+
autoCapitalize: "none",
|
|
1286
|
+
spellCheck: false,
|
|
1287
|
+
style: { ...inputStyle, fontFamily: MONO }
|
|
1288
|
+
}
|
|
1289
|
+
),
|
|
1290
|
+
err && /* @__PURE__ */ jsxRuntime.jsx(ErrorLine, { children: err }),
|
|
1291
|
+
/* @__PURE__ */ jsxRuntime.jsxs(FormButtons, { children: [
|
|
1292
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1293
|
+
"button",
|
|
1294
|
+
{
|
|
1295
|
+
type: "button",
|
|
1296
|
+
onClick: () => void getChallenge(),
|
|
1297
|
+
disabled: busy || addr.length < 14,
|
|
1298
|
+
style: primaryBtnStyle(busy || addr.length < 14),
|
|
1299
|
+
children: busy ? "requesting\u2026" : "get challenge"
|
|
1300
|
+
}
|
|
1301
|
+
),
|
|
1302
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: onCancel, style: ghostBtnStyle(busy), children: "cancel" })
|
|
1303
|
+
] })
|
|
1304
|
+
] }) : pendingSig ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
1305
|
+
TransferConfirm,
|
|
1306
|
+
{
|
|
1307
|
+
what: "bitcoin address",
|
|
1308
|
+
busy,
|
|
1309
|
+
onConfirm: () => void postLink(pendingSig, true),
|
|
1310
|
+
onCancel,
|
|
1311
|
+
err
|
|
1312
|
+
}
|
|
1313
|
+
) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1314
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { style: bodyStyle, children: [
|
|
1315
|
+
"Sign the challenge with the wallet that controls",
|
|
1316
|
+
" ",
|
|
1317
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontFamily: MONO, wordBreak: "break-all" }, children: addr }),
|
|
1318
|
+
"."
|
|
1319
|
+
] }),
|
|
1320
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1321
|
+
LazyWalletButton,
|
|
1322
|
+
{
|
|
1323
|
+
address: addr,
|
|
1324
|
+
message: challenge,
|
|
1325
|
+
showManual: true,
|
|
1326
|
+
layout: "list",
|
|
1327
|
+
heading: null,
|
|
1328
|
+
onSigned: (sig) => void postLink(sig, false),
|
|
1329
|
+
onError: (e) => setErr(e?.message ?? "wallet rejected")
|
|
1330
|
+
}
|
|
1331
|
+
),
|
|
1332
|
+
err && /* @__PURE__ */ jsxRuntime.jsx(ErrorLine, { children: err }),
|
|
1333
|
+
/* @__PURE__ */ jsxRuntime.jsx(FormButtons, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1334
|
+
"button",
|
|
1335
|
+
{
|
|
1336
|
+
type: "button",
|
|
1337
|
+
onClick: () => {
|
|
1338
|
+
setStep("request");
|
|
1339
|
+
setErr(null);
|
|
1340
|
+
},
|
|
1341
|
+
disabled: busy,
|
|
1342
|
+
style: ghostBtnStyle(busy),
|
|
1343
|
+
children: "back"
|
|
1344
|
+
}
|
|
1345
|
+
) })
|
|
1346
|
+
] })
|
|
1347
|
+
] });
|
|
1348
|
+
}
|
|
1349
|
+
function TransferConfirm({
|
|
1350
|
+
what,
|
|
1351
|
+
busy,
|
|
1352
|
+
onConfirm,
|
|
1353
|
+
onCancel,
|
|
1354
|
+
err
|
|
1355
|
+
}) {
|
|
1356
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1357
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { style: bodyStyle, children: [
|
|
1358
|
+
"This ",
|
|
1359
|
+
what,
|
|
1360
|
+
" 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?"
|
|
1361
|
+
] }),
|
|
1362
|
+
err && /* @__PURE__ */ jsxRuntime.jsx(ErrorLine, { children: err }),
|
|
1363
|
+
/* @__PURE__ */ jsxRuntime.jsxs(FormButtons, { children: [
|
|
1364
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1365
|
+
"button",
|
|
1366
|
+
{
|
|
1367
|
+
type: "button",
|
|
1368
|
+
onClick: onConfirm,
|
|
1369
|
+
disabled: busy,
|
|
1370
|
+
style: dangerBtnStyle(busy),
|
|
1371
|
+
children: busy ? "transferring\u2026" : "transfer link here"
|
|
1372
|
+
}
|
|
1373
|
+
),
|
|
1374
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: onCancel, disabled: busy, style: ghostBtnStyle(busy), children: "cancel" })
|
|
1375
|
+
] })
|
|
1376
|
+
] });
|
|
1377
|
+
}
|
|
1378
|
+
function OcIdentityBond({
|
|
1379
|
+
didOc,
|
|
1380
|
+
btc,
|
|
1381
|
+
className
|
|
1382
|
+
}) {
|
|
1383
|
+
const [stage, setStage] = React3__namespace.useState("idle");
|
|
1384
|
+
const [message, setMessage] = React3__namespace.useState("");
|
|
1385
|
+
const [err, setErr] = React3__namespace.useState(null);
|
|
1386
|
+
const [result, setResult] = React3__namespace.useState(null);
|
|
1387
|
+
function nip07() {
|
|
1388
|
+
if (typeof window === "undefined") return null;
|
|
1389
|
+
const w = window;
|
|
1390
|
+
return w.nostr ?? null;
|
|
1391
|
+
}
|
|
1392
|
+
async function begin() {
|
|
1393
|
+
setErr(null);
|
|
1394
|
+
const nostr = nip07();
|
|
1395
|
+
if (!nostr) {
|
|
1396
|
+
setErr(
|
|
1397
|
+
"no Nostr signer found \xB7 install a NIP-07 extension (Alby, nos2x) to counter-sign the bond"
|
|
1398
|
+
);
|
|
1399
|
+
return;
|
|
1400
|
+
}
|
|
1401
|
+
try {
|
|
1402
|
+
const sdk = await loadSdk();
|
|
1403
|
+
const pubkeyHex = (await nostr.getPublicKey()).toLowerCase();
|
|
1404
|
+
const npub = sdk.xOnlyHexToNpub(pubkeyHex);
|
|
1405
|
+
const msg = sdk.buildBindingMessage({
|
|
1406
|
+
principal: didOc,
|
|
1407
|
+
btc,
|
|
1408
|
+
nostr: npub,
|
|
1409
|
+
nonce: randomNonceHex(),
|
|
1410
|
+
issued_at: (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z")
|
|
1411
|
+
});
|
|
1412
|
+
setMessage(msg);
|
|
1413
|
+
setStage("sign-btc");
|
|
1414
|
+
} catch (e) {
|
|
1415
|
+
setErr(e instanceof Error ? e.message : "could not start the bond ceremony");
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
async function afterBtcSign(btcSignature) {
|
|
1419
|
+
setErr(null);
|
|
1420
|
+
setStage("sign-nostr");
|
|
1421
|
+
try {
|
|
1422
|
+
const nostr = nip07();
|
|
1423
|
+
if (!nostr) throw new Error("Nostr extension went away \xB7 retry");
|
|
1424
|
+
const sdk = await loadSdk();
|
|
1425
|
+
const pubkeyHex = (await nostr.getPublicKey()).toLowerCase();
|
|
1426
|
+
const template = sdk.buildBindingEventTemplate({
|
|
1427
|
+
message,
|
|
1428
|
+
btcSignature,
|
|
1429
|
+
nostrPubkeyHex: pubkeyHex,
|
|
1430
|
+
btc
|
|
1431
|
+
});
|
|
1432
|
+
const signedEvent = await nostr.signEvent(template);
|
|
1433
|
+
const envelope = sdk.assembleBindingEnvelope({
|
|
1434
|
+
message,
|
|
1435
|
+
btcSignature,
|
|
1436
|
+
btcScheme: "bip322",
|
|
1437
|
+
nostrEvent: signedEvent
|
|
1438
|
+
});
|
|
1439
|
+
const check = sdk.verifyBinding(envelope);
|
|
1440
|
+
if (!check.valid) {
|
|
1441
|
+
throw new Error(`assembled bond failed local verification (${check.status})`);
|
|
1442
|
+
}
|
|
1443
|
+
setStage("publishing");
|
|
1444
|
+
const { publishEvent } = await loadNostrCore();
|
|
1445
|
+
const results = await publishEvent(signedEvent);
|
|
1446
|
+
const ok = results.filter((r) => r.ok).length;
|
|
1447
|
+
if (ok === 0) {
|
|
1448
|
+
throw new Error("no relay accepted the bond \xB7 check your connection and retry");
|
|
1449
|
+
}
|
|
1450
|
+
setResult({
|
|
1451
|
+
bindingId: check.valid ? check.binding_id : sdk.bindingId(message),
|
|
1452
|
+
relays: ok,
|
|
1453
|
+
relaysTotal: results.length
|
|
1454
|
+
});
|
|
1455
|
+
setStage("done");
|
|
1456
|
+
} catch (e) {
|
|
1457
|
+
setErr(e instanceof Error ? e.message : "bond publish failed");
|
|
1458
|
+
setStage("sign-btc");
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, style: panelStyle("accent"), "data-oc-identity-bond": "", children: [
|
|
1462
|
+
/* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { children: "\xA7 identity bond \xB7 publish to nostr" }),
|
|
1463
|
+
stage === "idle" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1464
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { style: bodyStyle, children: [
|
|
1465
|
+
"Publish a ",
|
|
1466
|
+
/* @__PURE__ */ jsxRuntime.jsx("strong", { children: "Binding Attestation" }),
|
|
1467
|
+
" \u2014 a Bitcoin-signed, Nostr-signed proof that this address and your Nostr key are one identity. It lives on Nostr, verifies with zero trust in OrangeCheck, and means your identity survives even if OC's database does not. Two signatures: your Bitcoin wallet, then your Nostr extension."
|
|
1468
|
+
] }),
|
|
1469
|
+
/* @__PURE__ */ jsxRuntime.jsx(FormButtons, { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1470
|
+
"button",
|
|
1471
|
+
{
|
|
1472
|
+
type: "button",
|
|
1473
|
+
onClick: () => void begin(),
|
|
1474
|
+
style: primaryBtnStyle(false),
|
|
1475
|
+
children: "create & publish bond"
|
|
1476
|
+
}
|
|
1477
|
+
) })
|
|
1478
|
+
] }),
|
|
1479
|
+
stage === "sign-btc" && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1480
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { style: bodyStyle, children: [
|
|
1481
|
+
"Step 1 of 2 \u2014 sign the binding message with the Bitcoin wallet that controls ",
|
|
1482
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontFamily: MONO, wordBreak: "break-all" }, children: btc }),
|
|
1483
|
+
"."
|
|
1484
|
+
] }),
|
|
1485
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1486
|
+
LazyWalletButton,
|
|
1487
|
+
{
|
|
1488
|
+
address: btc,
|
|
1489
|
+
message,
|
|
1490
|
+
showManual: true,
|
|
1491
|
+
layout: "list",
|
|
1492
|
+
heading: null,
|
|
1493
|
+
onSigned: (sig) => void afterBtcSign(sig),
|
|
1494
|
+
onError: (e) => setErr(e?.message ?? "wallet rejected the signature")
|
|
1495
|
+
}
|
|
1496
|
+
)
|
|
1497
|
+
] }),
|
|
1498
|
+
(stage === "sign-nostr" || stage === "publishing") && /* @__PURE__ */ jsxRuntime.jsx("p", { style: bodyStyle, children: stage === "sign-nostr" ? "Step 2 of 2 \u2014 approve the signature in your Nostr extension\u2026" : "Publishing the bond to Nostr\u2026" }),
|
|
1499
|
+
stage === "done" && result && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
1500
|
+
/* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { tone: "success", children: "\xA7 bond published" }),
|
|
1501
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { style: bodyStyle, children: [
|
|
1502
|
+
"Your identity bond is live on ",
|
|
1503
|
+
result.relays,
|
|
1504
|
+
"/",
|
|
1505
|
+
result.relaysTotal,
|
|
1506
|
+
" ",
|
|
1507
|
+
"relay",
|
|
1508
|
+
result.relays === 1 ? "" : "s",
|
|
1509
|
+
". Anyone can now verify the BTC \u21C4 Nostr binding offline."
|
|
1510
|
+
] }),
|
|
1511
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1512
|
+
"div",
|
|
1513
|
+
{
|
|
1514
|
+
style: {
|
|
1515
|
+
fontFamily: MONO,
|
|
1516
|
+
fontSize: 11,
|
|
1517
|
+
color: V.muted,
|
|
1518
|
+
wordBreak: "break-all"
|
|
1519
|
+
},
|
|
1520
|
+
children: [
|
|
1521
|
+
"binding_id: ",
|
|
1522
|
+
result.bindingId
|
|
1523
|
+
]
|
|
1524
|
+
}
|
|
1525
|
+
)
|
|
1526
|
+
] }),
|
|
1527
|
+
err && /* @__PURE__ */ jsxRuntime.jsx(ErrorLine, { children: err })
|
|
1528
|
+
] });
|
|
1529
|
+
}
|
|
1530
|
+
function randomNonceHex() {
|
|
1531
|
+
const b = new Uint8Array(16);
|
|
1532
|
+
if (typeof crypto !== "undefined" && crypto.getRandomValues) {
|
|
1533
|
+
crypto.getRandomValues(b);
|
|
1534
|
+
} else {
|
|
1535
|
+
for (let i = 0; i < 16; i++) b[i] = Math.floor(Math.random() * 256);
|
|
1536
|
+
}
|
|
1537
|
+
let out = "";
|
|
1538
|
+
for (const x of b) out += x.toString(16).padStart(2, "0");
|
|
1539
|
+
return out;
|
|
1540
|
+
}
|
|
1541
|
+
async function loadSdk() {
|
|
1542
|
+
try {
|
|
1543
|
+
return await import('@orangecheck/sdk');
|
|
1544
|
+
} catch {
|
|
1545
|
+
throw new Error("@orangecheck/sdk not installed \xB7 add it to your site to publish bonds");
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
async function loadNostrCore() {
|
|
1549
|
+
try {
|
|
1550
|
+
return await import('@orangecheck/nostr-core');
|
|
1551
|
+
} catch {
|
|
1552
|
+
throw new Error(
|
|
1553
|
+
"@orangecheck/nostr-core not installed \xB7 add it to your site to publish bonds"
|
|
1554
|
+
);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
function LazyWalletButton(props) {
|
|
1558
|
+
const [Comp, setComp] = React3__namespace.useState(null);
|
|
1559
|
+
const [failed, setFailed] = React3__namespace.useState(false);
|
|
1560
|
+
React3__namespace.useEffect(() => {
|
|
1561
|
+
let alive = true;
|
|
1562
|
+
import('@orangecheck/wallet-adapter/react').then((m) => {
|
|
1563
|
+
if (alive) {
|
|
1564
|
+
setComp(
|
|
1565
|
+
() => m.OcWalletButton
|
|
1566
|
+
);
|
|
1567
|
+
}
|
|
1568
|
+
}).catch(() => {
|
|
1569
|
+
if (alive) setFailed(true);
|
|
1570
|
+
});
|
|
1571
|
+
return () => {
|
|
1572
|
+
alive = false;
|
|
1573
|
+
};
|
|
1574
|
+
}, []);
|
|
1575
|
+
if (failed) {
|
|
1576
|
+
return /* @__PURE__ */ jsxRuntime.jsx(ErrorLine, { children: "@orangecheck/wallet-adapter not installed \xB7 add it to your site to link a wallet" });
|
|
1577
|
+
}
|
|
1578
|
+
if (!Comp) return /* @__PURE__ */ jsxRuntime.jsxs("span", { style: hintStyle, children: [
|
|
1579
|
+
"> ",
|
|
1580
|
+
"loading wallet options\u2026"
|
|
1581
|
+
] });
|
|
1582
|
+
return /* @__PURE__ */ jsxRuntime.jsx(Comp, { ...props });
|
|
1583
|
+
}
|
|
1584
|
+
function mask(value, kind) {
|
|
1585
|
+
if (kind === "email") {
|
|
1586
|
+
const [user, host] = value.split("@");
|
|
1587
|
+
if (!user || !host) return "\u2022\u2022\u2022\u2022";
|
|
1588
|
+
const m = user.length <= 2 ? "\u2022".repeat(user.length) : user[0] + "\u2022".repeat(user.length - 2) + user.slice(-1);
|
|
1589
|
+
return `${m}@${host}`;
|
|
1590
|
+
}
|
|
1591
|
+
if (value.length <= 12) return "\u2022".repeat(value.length);
|
|
1592
|
+
return `${value.slice(0, 6)}\u2026${value.slice(-4)}`;
|
|
1593
|
+
}
|
|
1594
|
+
function SectionLabel({
|
|
1595
|
+
children,
|
|
1596
|
+
tone = "primary"
|
|
1597
|
+
}) {
|
|
1598
|
+
const color = tone === "success" ? V.success : tone === "warn" ? V.warn : V.primary;
|
|
1599
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1600
|
+
"div",
|
|
1601
|
+
{
|
|
1602
|
+
style: {
|
|
1603
|
+
color,
|
|
1604
|
+
fontFamily: MONO,
|
|
1605
|
+
fontSize: 10,
|
|
1606
|
+
letterSpacing: "0.18em",
|
|
1607
|
+
textTransform: "uppercase",
|
|
1608
|
+
marginBottom: 8
|
|
1609
|
+
},
|
|
1610
|
+
children
|
|
1611
|
+
}
|
|
1612
|
+
);
|
|
1613
|
+
}
|
|
1614
|
+
function ErrorLine({ children }) {
|
|
1615
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1616
|
+
"p",
|
|
1617
|
+
{
|
|
1618
|
+
role: "alert",
|
|
1619
|
+
style: {
|
|
1620
|
+
margin: "8px 0 0",
|
|
1621
|
+
color: V.destructive,
|
|
1622
|
+
fontFamily: MONO,
|
|
1623
|
+
fontSize: 11
|
|
1624
|
+
},
|
|
1625
|
+
children
|
|
1626
|
+
}
|
|
1627
|
+
);
|
|
1628
|
+
}
|
|
1629
|
+
function FormButtons({ children }) {
|
|
1630
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 8, marginTop: 12 }, children });
|
|
1631
|
+
}
|
|
1632
|
+
var MONO = "ui-monospace, SFMono-Regular, monospace";
|
|
1633
|
+
var V = {
|
|
1634
|
+
primary: "var(--primary, #f97316)",
|
|
1635
|
+
foreground: "var(--foreground, #fafafa)",
|
|
1636
|
+
muted: "var(--muted-foreground, #a1a1aa)",
|
|
1637
|
+
background: "var(--background, #0a0a0a)",
|
|
1638
|
+
card: "var(--card, #111113)",
|
|
1639
|
+
border: "var(--border, #27272a)",
|
|
1640
|
+
destructive: "var(--destructive, #ef4444)",
|
|
1641
|
+
success: "var(--success, #22c55e)",
|
|
1642
|
+
warn: "var(--warning, #eab308)"
|
|
1643
|
+
};
|
|
1644
|
+
function panelStyle(tone = "default") {
|
|
1645
|
+
const base = {
|
|
1646
|
+
background: V.card,
|
|
1647
|
+
border: `1px solid ${V.border}`,
|
|
1648
|
+
padding: "1.25rem"
|
|
1649
|
+
};
|
|
1650
|
+
if (tone === "plain") return { ...base, padding: 0 };
|
|
1651
|
+
if (tone === "accent") return { ...base, borderColor: V.primary, background: "var(--card, #111113)" };
|
|
1652
|
+
if (tone === "success") return { ...base, borderColor: V.success };
|
|
1653
|
+
if (tone === "warn") return { ...base, borderColor: V.warn };
|
|
1654
|
+
return base;
|
|
1655
|
+
}
|
|
1656
|
+
var bodyStyle = {
|
|
1657
|
+
color: V.muted,
|
|
1658
|
+
fontSize: 13,
|
|
1659
|
+
lineHeight: 1.55,
|
|
1660
|
+
margin: "0 0 12px"
|
|
1661
|
+
};
|
|
1662
|
+
var hintStyle = {
|
|
1663
|
+
color: V.muted,
|
|
1664
|
+
fontFamily: MONO,
|
|
1665
|
+
fontSize: 10.5,
|
|
1666
|
+
opacity: 0.8
|
|
1667
|
+
};
|
|
1668
|
+
var codeStyle = {
|
|
1669
|
+
fontFamily: MONO,
|
|
1670
|
+
color: V.foreground
|
|
1671
|
+
};
|
|
1672
|
+
var rowStyle = {
|
|
1673
|
+
display: "flex",
|
|
1674
|
+
flexWrap: "wrap",
|
|
1675
|
+
alignItems: "center",
|
|
1676
|
+
justifyContent: "space-between",
|
|
1677
|
+
gap: 12,
|
|
1678
|
+
padding: "1rem"
|
|
1679
|
+
};
|
|
1680
|
+
var rowLabelStyle = {
|
|
1681
|
+
color: V.foreground,
|
|
1682
|
+
fontFamily: MONO,
|
|
1683
|
+
fontSize: 10.5,
|
|
1684
|
+
letterSpacing: "0.12em",
|
|
1685
|
+
textTransform: "uppercase",
|
|
1686
|
+
opacity: 0.8
|
|
1687
|
+
};
|
|
1688
|
+
var inputStyle = {
|
|
1689
|
+
width: "100%",
|
|
1690
|
+
padding: "0.55rem 0.75rem",
|
|
1691
|
+
background: V.background,
|
|
1692
|
+
color: V.foreground,
|
|
1693
|
+
border: `1px solid ${V.border}`,
|
|
1694
|
+
borderRadius: 6,
|
|
1695
|
+
fontFamily: MONO,
|
|
1696
|
+
// 16px — never lower · suppresses iOS Safari focus auto-zoom.
|
|
1697
|
+
fontSize: 16,
|
|
1698
|
+
outline: "none",
|
|
1699
|
+
boxSizing: "border-box"
|
|
1700
|
+
};
|
|
1701
|
+
function btnBase(disabled) {
|
|
1702
|
+
return {
|
|
1703
|
+
padding: "0.5rem 0.875rem",
|
|
1704
|
+
borderRadius: 6,
|
|
1705
|
+
fontFamily: MONO,
|
|
1706
|
+
fontSize: 10.5,
|
|
1707
|
+
letterSpacing: "0.12em",
|
|
1708
|
+
textTransform: "uppercase",
|
|
1709
|
+
cursor: disabled ? "not-allowed" : "pointer",
|
|
1710
|
+
opacity: disabled ? 0.5 : 1
|
|
1711
|
+
};
|
|
1712
|
+
}
|
|
1713
|
+
function primaryBtnStyle(disabled) {
|
|
1714
|
+
return {
|
|
1715
|
+
...btnBase(disabled),
|
|
1716
|
+
background: V.primary,
|
|
1717
|
+
color: "var(--primary-foreground, #0a0a0a)",
|
|
1718
|
+
border: `1px solid ${V.primary}`,
|
|
1719
|
+
fontWeight: 600
|
|
1720
|
+
};
|
|
1721
|
+
}
|
|
1722
|
+
function ghostBtnStyle(disabled) {
|
|
1723
|
+
return {
|
|
1724
|
+
...btnBase(disabled),
|
|
1725
|
+
background: V.background,
|
|
1726
|
+
color: V.foreground,
|
|
1727
|
+
border: `1px solid ${V.border}`
|
|
1728
|
+
};
|
|
1729
|
+
}
|
|
1730
|
+
function dangerBtnStyle(disabled) {
|
|
1731
|
+
return {
|
|
1732
|
+
...btnBase(disabled),
|
|
1733
|
+
background: "transparent",
|
|
1734
|
+
color: V.destructive,
|
|
1735
|
+
border: `1px solid ${V.destructive}`
|
|
1736
|
+
};
|
|
1737
|
+
}
|
|
1738
|
+
var primaryChipStyle = {
|
|
1739
|
+
...btnBase(false),
|
|
1740
|
+
background: "transparent",
|
|
1741
|
+
color: V.primary,
|
|
1742
|
+
border: `1px solid ${V.primary}`
|
|
1743
|
+
};
|
|
773
1744
|
function safeReturnTo(input) {
|
|
774
1745
|
const candidate = input ?? "/";
|
|
775
1746
|
if (typeof candidate !== "string") return "/";
|
|
@@ -785,6 +1756,7 @@ function OcSignIn({
|
|
|
785
1756
|
returnTo,
|
|
786
1757
|
onSuccess,
|
|
787
1758
|
resolveReturnTo,
|
|
1759
|
+
linkPrompt,
|
|
788
1760
|
authOrigin = "https://ochk.io",
|
|
789
1761
|
initialPath = "wallet",
|
|
790
1762
|
paths,
|
|
@@ -793,24 +1765,34 @@ function OcSignIn({
|
|
|
793
1765
|
const walletEnabled = paths?.wallet ?? true;
|
|
794
1766
|
const emailEnabled = paths?.email ?? true;
|
|
795
1767
|
const safeReturn = safeReturnTo(returnTo);
|
|
796
|
-
const [path, setPath] =
|
|
797
|
-
const
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
onSuccess(account, token);
|
|
801
|
-
return;
|
|
802
|
-
}
|
|
1768
|
+
const [path, setPath] = React3__namespace.useState(initialPath);
|
|
1769
|
+
const [signedIn, setSignedIn] = React3__namespace.useState(null);
|
|
1770
|
+
const navigate = React3__namespace.useCallback(
|
|
1771
|
+
async (account) => {
|
|
803
1772
|
if (resolveReturnTo) {
|
|
804
1773
|
try {
|
|
805
|
-
|
|
806
|
-
hardNavigate(safeReturnTo(target));
|
|
1774
|
+
hardNavigate(safeReturnTo(await resolveReturnTo(account)));
|
|
807
1775
|
return;
|
|
808
1776
|
} catch {
|
|
809
1777
|
}
|
|
810
1778
|
}
|
|
811
1779
|
hardNavigate(safeReturn);
|
|
812
1780
|
},
|
|
813
|
-
[
|
|
1781
|
+
[resolveReturnTo, safeReturn]
|
|
1782
|
+
);
|
|
1783
|
+
const handleSuccess = React3__namespace.useCallback(
|
|
1784
|
+
async (account, token) => {
|
|
1785
|
+
if (onSuccess) {
|
|
1786
|
+
onSuccess(account, token);
|
|
1787
|
+
return;
|
|
1788
|
+
}
|
|
1789
|
+
if (linkPrompt) {
|
|
1790
|
+
setSignedIn(account);
|
|
1791
|
+
return;
|
|
1792
|
+
}
|
|
1793
|
+
await navigate(account);
|
|
1794
|
+
},
|
|
1795
|
+
[onSuccess, linkPrompt, navigate]
|
|
814
1796
|
);
|
|
815
1797
|
if (!walletEnabled && !emailEnabled) {
|
|
816
1798
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -837,6 +1819,15 @@ function OcSignIn({
|
|
|
837
1819
|
}
|
|
838
1820
|
);
|
|
839
1821
|
}
|
|
1822
|
+
if (signedIn) {
|
|
1823
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className, "data-oc-signin": "", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1824
|
+
LinkPromptStep,
|
|
1825
|
+
{
|
|
1826
|
+
authOrigin,
|
|
1827
|
+
onContinue: () => void navigate(signedIn)
|
|
1828
|
+
}
|
|
1829
|
+
) });
|
|
1830
|
+
}
|
|
840
1831
|
const showWallet = walletEnabled && (path === "wallet" || !emailEnabled);
|
|
841
1832
|
const showEmail = emailEnabled && (path === "email" || !walletEnabled);
|
|
842
1833
|
const bothEnabled = walletEnabled && emailEnabled;
|
|
@@ -879,6 +1870,25 @@ function OcSignIn({
|
|
|
879
1870
|
] })
|
|
880
1871
|
] });
|
|
881
1872
|
}
|
|
1873
|
+
function LinkPromptStep({
|
|
1874
|
+
authOrigin,
|
|
1875
|
+
onContinue
|
|
1876
|
+
}) {
|
|
1877
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-oc-signin-linkprompt": "", children: [
|
|
1878
|
+
/* @__PURE__ */ jsxRuntime.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." }),
|
|
1879
|
+
/* @__PURE__ */ jsxRuntime.jsx(OcLinkedIdentities, { authOrigin }),
|
|
1880
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1881
|
+
"button",
|
|
1882
|
+
{
|
|
1883
|
+
type: "button",
|
|
1884
|
+
onClick: onContinue,
|
|
1885
|
+
style: submitStyle(false),
|
|
1886
|
+
"data-oc-signin-continue": "",
|
|
1887
|
+
children: "continue \u2192"
|
|
1888
|
+
}
|
|
1889
|
+
)
|
|
1890
|
+
] });
|
|
1891
|
+
}
|
|
882
1892
|
function SigninTab({
|
|
883
1893
|
active,
|
|
884
1894
|
onClick,
|
|
@@ -910,10 +1920,10 @@ function SigninTab({
|
|
|
910
1920
|
);
|
|
911
1921
|
}
|
|
912
1922
|
function WalletFlow({ authOrigin, audience, onSuccess }) {
|
|
913
|
-
const [address, setAddress] =
|
|
914
|
-
const [error, setError] =
|
|
915
|
-
const [submitting, setSubmitting] =
|
|
916
|
-
const [stage, setStage] =
|
|
1923
|
+
const [address, setAddress] = React3__namespace.useState("");
|
|
1924
|
+
const [error, setError] = React3__namespace.useState(null);
|
|
1925
|
+
const [submitting, setSubmitting] = React3__namespace.useState(false);
|
|
1926
|
+
const [stage, setStage] = React3__namespace.useState("enter");
|
|
917
1927
|
async function signIn(e) {
|
|
918
1928
|
e.preventDefault();
|
|
919
1929
|
const addr = address.trim();
|
|
@@ -992,10 +2002,10 @@ function WalletFlow({ authOrigin, audience, onSuccess }) {
|
|
|
992
2002
|
spellCheck: false,
|
|
993
2003
|
required: true,
|
|
994
2004
|
"aria-invalid": error ? true : void 0,
|
|
995
|
-
style:
|
|
2005
|
+
style: inputStyle2
|
|
996
2006
|
}
|
|
997
2007
|
),
|
|
998
|
-
error && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2008
|
+
error && /* @__PURE__ */ jsxRuntime.jsx(ErrorLine2, { children: error }),
|
|
999
2009
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1000
2010
|
"button",
|
|
1001
2011
|
{
|
|
@@ -1009,13 +2019,13 @@ function WalletFlow({ authOrigin, audience, onSuccess }) {
|
|
|
1009
2019
|
] });
|
|
1010
2020
|
}
|
|
1011
2021
|
function EmailFlow({ authOrigin, onSuccess }) {
|
|
1012
|
-
const [stage, setStage] =
|
|
1013
|
-
const [email, setEmail] =
|
|
1014
|
-
const [emailError, setEmailError] =
|
|
1015
|
-
const [code, setCode] =
|
|
1016
|
-
const [codeError, setCodeError] =
|
|
1017
|
-
const [token, setToken] =
|
|
1018
|
-
const [submitting, setSubmitting] =
|
|
2022
|
+
const [stage, setStage] = React3__namespace.useState("enter");
|
|
2023
|
+
const [email, setEmail] = React3__namespace.useState("");
|
|
2024
|
+
const [emailError, setEmailError] = React3__namespace.useState(null);
|
|
2025
|
+
const [code, setCode] = React3__namespace.useState("");
|
|
2026
|
+
const [codeError, setCodeError] = React3__namespace.useState(null);
|
|
2027
|
+
const [token, setToken] = React3__namespace.useState(null);
|
|
2028
|
+
const [submitting, setSubmitting] = React3__namespace.useState(false);
|
|
1019
2029
|
async function start(e) {
|
|
1020
2030
|
e.preventDefault();
|
|
1021
2031
|
const trimmed = email.trim().toLowerCase();
|
|
@@ -1091,10 +2101,10 @@ function EmailFlow({ authOrigin, onSuccess }) {
|
|
|
1091
2101
|
placeholder: "you@example.com",
|
|
1092
2102
|
required: true,
|
|
1093
2103
|
"aria-invalid": emailError ? true : void 0,
|
|
1094
|
-
style:
|
|
2104
|
+
style: inputStyle2
|
|
1095
2105
|
}
|
|
1096
2106
|
),
|
|
1097
|
-
emailError && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2107
|
+
emailError && /* @__PURE__ */ jsxRuntime.jsx(ErrorLine2, { children: emailError }),
|
|
1098
2108
|
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", disabled: submitting, style: submitStyle(submitting), children: submitting ? "sending\u2026" : "send one-time code \u2192" }),
|
|
1099
2109
|
/* @__PURE__ */ jsxRuntime.jsx(Hint, { children: "OTP delivered by the auth host. Codes expire in 10 minutes and are single-use. Rate-limited 5 starts/hour/IP." })
|
|
1100
2110
|
] });
|
|
@@ -1123,10 +2133,10 @@ function EmailFlow({ authOrigin, onSuccess }) {
|
|
|
1123
2133
|
required: true,
|
|
1124
2134
|
autoFocus: true,
|
|
1125
2135
|
"aria-invalid": codeError ? true : void 0,
|
|
1126
|
-
style: { ...
|
|
2136
|
+
style: { ...inputStyle2, letterSpacing: "0.4em", fontSize: 16 }
|
|
1127
2137
|
}
|
|
1128
2138
|
),
|
|
1129
|
-
codeError && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2139
|
+
codeError && /* @__PURE__ */ jsxRuntime.jsx(ErrorLine2, { children: codeError }),
|
|
1130
2140
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1131
2141
|
"button",
|
|
1132
2142
|
{
|
|
@@ -1213,7 +2223,7 @@ function Label({ children }) {
|
|
|
1213
2223
|
}
|
|
1214
2224
|
);
|
|
1215
2225
|
}
|
|
1216
|
-
var
|
|
2226
|
+
var inputStyle2 = {
|
|
1217
2227
|
width: "100%",
|
|
1218
2228
|
padding: "0.55rem 0.75rem",
|
|
1219
2229
|
background: "var(--background, #0a0a0a)",
|
|
@@ -1245,7 +2255,7 @@ function submitStyle(disabled) {
|
|
|
1245
2255
|
width: "100%"
|
|
1246
2256
|
};
|
|
1247
2257
|
}
|
|
1248
|
-
function
|
|
2258
|
+
function ErrorLine2({ children }) {
|
|
1249
2259
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1250
2260
|
"p",
|
|
1251
2261
|
{
|
|
@@ -1292,13 +2302,13 @@ function isAbortError(err) {
|
|
|
1292
2302
|
}
|
|
1293
2303
|
function useWebAuthnRegister(opts) {
|
|
1294
2304
|
const authOrigin = resolveAuthOrigin(opts);
|
|
1295
|
-
const [status, setStatus] =
|
|
1296
|
-
const [error, setError] =
|
|
1297
|
-
const reset =
|
|
2305
|
+
const [status, setStatus] = React3__namespace.useState("idle");
|
|
2306
|
+
const [error, setError] = React3__namespace.useState(null);
|
|
2307
|
+
const reset = React3__namespace.useCallback(() => {
|
|
1298
2308
|
setStatus("idle");
|
|
1299
2309
|
setError(null);
|
|
1300
2310
|
}, []);
|
|
1301
|
-
const register =
|
|
2311
|
+
const register = React3__namespace.useCallback(
|
|
1302
2312
|
async (args) => {
|
|
1303
2313
|
setError(null);
|
|
1304
2314
|
setStatus("requesting-options");
|
|
@@ -1360,10 +2370,10 @@ function useWebAuthnRegister(opts) {
|
|
|
1360
2370
|
function useWebAuthnList(opts) {
|
|
1361
2371
|
const authOrigin = resolveAuthOrigin(opts);
|
|
1362
2372
|
const { status: sessionStatus } = useOcSession();
|
|
1363
|
-
const [credentials, setCredentials] =
|
|
1364
|
-
const [status, setStatus] =
|
|
1365
|
-
const [error, setError] =
|
|
1366
|
-
const refetch =
|
|
2373
|
+
const [credentials, setCredentials] = React3__namespace.useState([]);
|
|
2374
|
+
const [status, setStatus] = React3__namespace.useState("loading");
|
|
2375
|
+
const [error, setError] = React3__namespace.useState(null);
|
|
2376
|
+
const refetch = React3__namespace.useCallback(async () => {
|
|
1367
2377
|
setError(null);
|
|
1368
2378
|
if (sessionStatus !== "authenticated") {
|
|
1369
2379
|
setCredentials([]);
|
|
@@ -1391,10 +2401,10 @@ function useWebAuthnList(opts) {
|
|
|
1391
2401
|
setStatus("error");
|
|
1392
2402
|
}
|
|
1393
2403
|
}, [authOrigin, sessionStatus]);
|
|
1394
|
-
|
|
2404
|
+
React3__namespace.useEffect(() => {
|
|
1395
2405
|
void refetch();
|
|
1396
2406
|
}, [refetch]);
|
|
1397
|
-
const rename =
|
|
2407
|
+
const rename = React3__namespace.useCallback(
|
|
1398
2408
|
async (id, label) => {
|
|
1399
2409
|
try {
|
|
1400
2410
|
const r = await fetch(`${authOrigin}/api/auth/webauthn/credentials/${id}`, {
|
|
@@ -1419,7 +2429,7 @@ function useWebAuthnList(opts) {
|
|
|
1419
2429
|
},
|
|
1420
2430
|
[authOrigin, refetch]
|
|
1421
2431
|
);
|
|
1422
|
-
const remove =
|
|
2432
|
+
const remove = React3__namespace.useCallback(
|
|
1423
2433
|
async (id) => {
|
|
1424
2434
|
try {
|
|
1425
2435
|
const r = await fetch(`${authOrigin}/api/auth/webauthn/credentials/${id}`, {
|
|
@@ -1447,13 +2457,13 @@ function useWebAuthnList(opts) {
|
|
|
1447
2457
|
function useStepUpAuth(opts) {
|
|
1448
2458
|
const authOrigin = resolveAuthOrigin(opts);
|
|
1449
2459
|
const { refresh } = useOcSession();
|
|
1450
|
-
const [status, setStatus] =
|
|
1451
|
-
const [error, setError] =
|
|
1452
|
-
const reset =
|
|
2460
|
+
const [status, setStatus] = React3__namespace.useState("idle");
|
|
2461
|
+
const [error, setError] = React3__namespace.useState(null);
|
|
2462
|
+
const reset = React3__namespace.useCallback(() => {
|
|
1453
2463
|
setStatus("idle");
|
|
1454
2464
|
setError(null);
|
|
1455
2465
|
}, []);
|
|
1456
|
-
const stepUp =
|
|
2466
|
+
const stepUp = React3__namespace.useCallback(
|
|
1457
2467
|
async (args) => {
|
|
1458
2468
|
setError(null);
|
|
1459
2469
|
setStatus("requesting-options");
|
|
@@ -1542,6 +2552,8 @@ exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
|
|
|
1542
2552
|
exports.OcAccountChip = OcAccountChip;
|
|
1543
2553
|
exports.OcAccountPill = OcAccountPill;
|
|
1544
2554
|
exports.OcAddressInput = OcAddressInput;
|
|
2555
|
+
exports.OcIdentityBond = OcIdentityBond;
|
|
2556
|
+
exports.OcLinkedIdentities = OcLinkedIdentities;
|
|
1545
2557
|
exports.OcSessionProvider = OcSessionProvider;
|
|
1546
2558
|
exports.OcSignIn = OcSignIn;
|
|
1547
2559
|
exports.OcSignInButton = OcSignInButton;
|