@orangecheck/auth-client 2.25.3 → 2.27.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 +2 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +559 -318
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +558 -317
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var React4 = 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 React4__namespace = /*#__PURE__*/_interopNamespace(React4);
|
|
26
26
|
|
|
27
27
|
// src/provider.tsx
|
|
28
28
|
|
|
@@ -221,7 +221,7 @@ async function consumeTabAccountHint(authOrigin) {
|
|
|
221
221
|
}
|
|
222
222
|
return null;
|
|
223
223
|
}
|
|
224
|
-
var SessionContext =
|
|
224
|
+
var SessionContext = React4__namespace.createContext(null);
|
|
225
225
|
function normalizeDisplayIdentity(raw, didOc) {
|
|
226
226
|
const di = raw.display_identity ?? raw.displayIdentity;
|
|
227
227
|
if (di && typeof di === "object" && typeof di.value === "string" && di.value.length > 0 && typeof di.kind === "string" && DISPLAY_IDENTITY_KINDS.includes(di.kind)) {
|
|
@@ -279,14 +279,14 @@ function OcSessionProvider({
|
|
|
279
279
|
config,
|
|
280
280
|
defaultReturnTo
|
|
281
281
|
}) {
|
|
282
|
-
const cfg =
|
|
283
|
-
const [account, setAccount] =
|
|
284
|
-
const [roster, setRoster] =
|
|
285
|
-
const [status, setStatus] =
|
|
286
|
-
const [error, setError] =
|
|
287
|
-
const [tabPinned, setTabPinned] =
|
|
288
|
-
const pinInFlightRef =
|
|
289
|
-
const pinThisTab =
|
|
282
|
+
const cfg = React4__namespace.useMemo(() => resolveConfig(config), [config]);
|
|
283
|
+
const [account, setAccount] = React4__namespace.useState(null);
|
|
284
|
+
const [roster, setRoster] = React4__namespace.useState([]);
|
|
285
|
+
const [status, setStatus] = React4__namespace.useState("loading");
|
|
286
|
+
const [error, setError] = React4__namespace.useState(null);
|
|
287
|
+
const [tabPinned, setTabPinned] = React4__namespace.useState(false);
|
|
288
|
+
const pinInFlightRef = React4__namespace.useRef(false);
|
|
289
|
+
const pinThisTab = React4__namespace.useCallback(
|
|
290
290
|
async (didOc) => {
|
|
291
291
|
if (pinInFlightRef.current || readTabSession()) return;
|
|
292
292
|
pinInFlightRef.current = true;
|
|
@@ -309,7 +309,7 @@ function OcSessionProvider({
|
|
|
309
309
|
},
|
|
310
310
|
[cfg.authOrigin]
|
|
311
311
|
);
|
|
312
|
-
const refresh =
|
|
312
|
+
const refresh = React4__namespace.useCallback(async () => {
|
|
313
313
|
if (typeof window === "undefined") return;
|
|
314
314
|
try {
|
|
315
315
|
let pin = readTabSession();
|
|
@@ -363,7 +363,7 @@ function OcSessionProvider({
|
|
|
363
363
|
setError(err instanceof Error ? err : new Error(String(err)));
|
|
364
364
|
}
|
|
365
365
|
}, [cfg, pinThisTab]);
|
|
366
|
-
|
|
366
|
+
React4__namespace.useEffect(() => {
|
|
367
367
|
let cancelled = false;
|
|
368
368
|
consumeTabAdoptMarker();
|
|
369
369
|
void (async () => {
|
|
@@ -374,9 +374,9 @@ function OcSessionProvider({
|
|
|
374
374
|
cancelled = true;
|
|
375
375
|
};
|
|
376
376
|
}, [refresh, cfg.authOrigin]);
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
const signOut =
|
|
377
|
+
React4__namespace.useEffect(() => installTabFetchInterceptor(cfg.authOrigin), [cfg.authOrigin]);
|
|
378
|
+
React4__namespace.useEffect(() => installTabLinkDecorator(cfg.authOrigin), [cfg.authOrigin]);
|
|
379
|
+
const signOut = React4__namespace.useCallback(
|
|
380
380
|
async (opts) => {
|
|
381
381
|
const scope = opts?.scope ?? "all";
|
|
382
382
|
try {
|
|
@@ -412,7 +412,7 @@ function OcSessionProvider({
|
|
|
412
412
|
},
|
|
413
413
|
[cfg.authOrigin, cfg.logoutPath, refresh]
|
|
414
414
|
);
|
|
415
|
-
const switchAccount =
|
|
415
|
+
const switchAccount = React4__namespace.useCallback(
|
|
416
416
|
async (didOc) => {
|
|
417
417
|
if (typeof window === "undefined") return;
|
|
418
418
|
const res = await fetch(`${cfg.authOrigin}/api/auth/switch`, {
|
|
@@ -442,14 +442,14 @@ function OcSessionProvider({
|
|
|
442
442
|
},
|
|
443
443
|
[cfg.authOrigin, refresh]
|
|
444
444
|
);
|
|
445
|
-
const addAccountUrl =
|
|
445
|
+
const addAccountUrl = React4__namespace.useCallback(
|
|
446
446
|
(returnTo) => {
|
|
447
447
|
const rt = returnTo ?? (typeof window !== "undefined" ? window.location.href : void 0);
|
|
448
448
|
return buildAddAccountUrl(cfg, rt);
|
|
449
449
|
},
|
|
450
450
|
[cfg]
|
|
451
451
|
);
|
|
452
|
-
const setDisplayIdentity =
|
|
452
|
+
const setDisplayIdentity = React4__namespace.useCallback(
|
|
453
453
|
async (kind) => {
|
|
454
454
|
if (typeof window === "undefined") return;
|
|
455
455
|
const res = await fetch(`${cfg.authOrigin}/api/auth/account`, {
|
|
@@ -479,7 +479,7 @@ function OcSessionProvider({
|
|
|
479
479
|
},
|
|
480
480
|
[cfg.authOrigin, refresh]
|
|
481
481
|
);
|
|
482
|
-
const value =
|
|
482
|
+
const value = React4__namespace.useMemo(() => {
|
|
483
483
|
const returnTo = defaultReturnTo ?? (typeof window !== "undefined" ? window.location.href : void 0);
|
|
484
484
|
return {
|
|
485
485
|
status,
|
|
@@ -511,7 +511,7 @@ function OcSessionProvider({
|
|
|
511
511
|
return /* @__PURE__ */ jsxRuntime.jsx(SessionContext.Provider, { value, children });
|
|
512
512
|
}
|
|
513
513
|
function useOcSession() {
|
|
514
|
-
const ctx =
|
|
514
|
+
const ctx = React4__namespace.useContext(SessionContext);
|
|
515
515
|
if (!ctx) {
|
|
516
516
|
throw new Error(
|
|
517
517
|
"[@orangecheck/auth-client] useOcSession() must be called inside <OcSessionProvider>"
|
|
@@ -520,7 +520,7 @@ function useOcSession() {
|
|
|
520
520
|
return ctx;
|
|
521
521
|
}
|
|
522
522
|
function useOptionalOcSession() {
|
|
523
|
-
return
|
|
523
|
+
return React4__namespace.useContext(SessionContext);
|
|
524
524
|
}
|
|
525
525
|
function shortenAddress(addr) {
|
|
526
526
|
if (addr.length <= 12) return addr;
|
|
@@ -535,7 +535,7 @@ function isPrefixOf(value, target) {
|
|
|
535
535
|
}
|
|
536
536
|
var listboxIdCounter = 0;
|
|
537
537
|
function useUniqueId(prefix) {
|
|
538
|
-
const [id] =
|
|
538
|
+
const [id] = React4__namespace.useState(() => `${prefix}-${++listboxIdCounter}`);
|
|
539
539
|
return id;
|
|
540
540
|
}
|
|
541
541
|
function OcAccountChip({
|
|
@@ -549,9 +549,9 @@ function OcAccountChip({
|
|
|
549
549
|
}) {
|
|
550
550
|
const { status, account, signInUrl: sessionSignInUrl, signOut } = useOcSession();
|
|
551
551
|
const signInUrl = signInUrlOverride ?? sessionSignInUrl;
|
|
552
|
-
const [open, setOpen] =
|
|
553
|
-
const wrapRef =
|
|
554
|
-
|
|
552
|
+
const [open, setOpen] = React4__namespace.useState(false);
|
|
553
|
+
const wrapRef = React4__namespace.useRef(null);
|
|
554
|
+
React4__namespace.useEffect(() => {
|
|
555
555
|
if (!open) return;
|
|
556
556
|
function handleClickOutside(e) {
|
|
557
557
|
if (wrapRef.current && !wrapRef.current.contains(e.target)) {
|
|
@@ -874,9 +874,9 @@ function useOcAddressSuggestion(options) {
|
|
|
874
874
|
} = options;
|
|
875
875
|
const { status, account } = useOcSession();
|
|
876
876
|
const sessionAddress = status === "authenticated" ? account?.primaryBtc ?? null : null;
|
|
877
|
-
const [open, setOpen] =
|
|
878
|
-
const [highlighted, setHighlighted] =
|
|
879
|
-
const blurTimer =
|
|
877
|
+
const [open, setOpen] = React4__namespace.useState(false);
|
|
878
|
+
const [highlighted, setHighlighted] = React4__namespace.useState(false);
|
|
879
|
+
const blurTimer = React4__namespace.useRef(null);
|
|
880
880
|
const listboxId = useUniqueId("oc-addr-listbox");
|
|
881
881
|
const optionId = `${listboxId}-opt`;
|
|
882
882
|
const valueMatchesSession = sessionAddress != null && value.toLowerCase() === sessionAddress.toLowerCase();
|
|
@@ -888,7 +888,7 @@ function useOcAddressSuggestion(options) {
|
|
|
888
888
|
setOpen(false);
|
|
889
889
|
setHighlighted(false);
|
|
890
890
|
}
|
|
891
|
-
|
|
891
|
+
React4__namespace.useEffect(() => {
|
|
892
892
|
return () => {
|
|
893
893
|
if (blurTimer.current != null) window.clearTimeout(blurTimer.current);
|
|
894
894
|
};
|
|
@@ -991,7 +991,7 @@ function useOcAddressSuggestion(options) {
|
|
|
991
991
|
) : null;
|
|
992
992
|
return { inputProps, popover };
|
|
993
993
|
}
|
|
994
|
-
var OcAddressInput =
|
|
994
|
+
var OcAddressInput = React4__namespace.forwardRef(
|
|
995
995
|
function OcAddressInput2({
|
|
996
996
|
value,
|
|
997
997
|
onValueChange,
|
|
@@ -1006,15 +1006,15 @@ var OcAddressInput = React3__namespace.forwardRef(
|
|
|
1006
1006
|
}, forwardedRef) {
|
|
1007
1007
|
const { status, account } = useOcSession();
|
|
1008
1008
|
const sessionAddress = status === "authenticated" ? account?.primaryBtc ?? null : null;
|
|
1009
|
-
const [open, setOpen] =
|
|
1010
|
-
const [highlighted, setHighlighted] =
|
|
1011
|
-
const innerRef =
|
|
1009
|
+
const [open, setOpen] = React4__namespace.useState(false);
|
|
1010
|
+
const [highlighted, setHighlighted] = React4__namespace.useState(false);
|
|
1011
|
+
const innerRef = React4__namespace.useRef(null);
|
|
1012
1012
|
const setRef = (node) => {
|
|
1013
1013
|
innerRef.current = node;
|
|
1014
1014
|
if (typeof forwardedRef === "function") forwardedRef(node);
|
|
1015
1015
|
else if (forwardedRef) forwardedRef.current = node;
|
|
1016
1016
|
};
|
|
1017
|
-
const blurTimer =
|
|
1017
|
+
const blurTimer = React4__namespace.useRef(null);
|
|
1018
1018
|
const listboxId = useUniqueId("oc-addr-listbox");
|
|
1019
1019
|
const optionId = `${listboxId}-opt`;
|
|
1020
1020
|
const valueMatchesSession = sessionAddress != null && value.toLowerCase() === sessionAddress.toLowerCase();
|
|
@@ -1061,7 +1061,7 @@ var OcAddressInput = React3__namespace.forwardRef(
|
|
|
1061
1061
|
}
|
|
1062
1062
|
onKeyDown?.(e);
|
|
1063
1063
|
}
|
|
1064
|
-
|
|
1064
|
+
React4__namespace.useEffect(() => {
|
|
1065
1065
|
return () => {
|
|
1066
1066
|
if (blurTimer.current != null) window.clearTimeout(blurTimer.current);
|
|
1067
1067
|
};
|
|
@@ -1180,6 +1180,15 @@ var SUDO_ACCOUNT_MISMATCH_MESSAGE = "Switch this tab to that account first \u201
|
|
|
1180
1180
|
function isSudoAccountMismatch(body) {
|
|
1181
1181
|
return body?.reason === "sudo_account_mismatch";
|
|
1182
1182
|
}
|
|
1183
|
+
|
|
1184
|
+
// src/appearance.ts
|
|
1185
|
+
var SIGNIN_FONT = "var(--oc-signin-font, ui-monospace, SFMono-Regular, monospace)";
|
|
1186
|
+
var SIGNIN_CASE = "var(--oc-signin-case, uppercase)";
|
|
1187
|
+
var signinTracking = (em) => `var(--oc-signin-tracking, ${em})`;
|
|
1188
|
+
var signinSize = (px) => `calc(${px}px * var(--oc-signin-scale, 1))`;
|
|
1189
|
+
var signinRadius = (px) => `var(--oc-signin-radius, ${px}px)`;
|
|
1190
|
+
var SIGNIN_BUTTON_RADIUS = "var(--oc-signin-button-radius, 6px)";
|
|
1191
|
+
var SIGNIN_STRONG = "var(--oc-signin-strong, 600)";
|
|
1183
1192
|
var DEFAULT_AUTH_ORIGIN = "https://ochk.io";
|
|
1184
1193
|
async function fetchOcLinkedIdentities(opts) {
|
|
1185
1194
|
const authOrigin = opts?.authOrigin ?? DEFAULT_AUTH_ORIGIN;
|
|
@@ -1195,13 +1204,14 @@ async function fetchOcLinkedIdentities(opts) {
|
|
|
1195
1204
|
function OcLinkedIdentities({
|
|
1196
1205
|
authOrigin = DEFAULT_AUTH_ORIGIN,
|
|
1197
1206
|
className,
|
|
1198
|
-
onChange
|
|
1207
|
+
onChange,
|
|
1208
|
+
attestationLink = true
|
|
1199
1209
|
}) {
|
|
1200
|
-
const [resp, setResp] =
|
|
1201
|
-
const [error, setError] =
|
|
1202
|
-
const [showEmailLink, setShowEmailLink] =
|
|
1203
|
-
const [showBtcLink, setShowBtcLink] =
|
|
1204
|
-
const refresh =
|
|
1210
|
+
const [resp, setResp] = React4__namespace.useState(null);
|
|
1211
|
+
const [error, setError] = React4__namespace.useState(null);
|
|
1212
|
+
const [showEmailLink, setShowEmailLink] = React4__namespace.useState(false);
|
|
1213
|
+
const [showBtcLink, setShowBtcLink] = React4__namespace.useState(false);
|
|
1214
|
+
const refresh = React4__namespace.useCallback(async () => {
|
|
1205
1215
|
setError(null);
|
|
1206
1216
|
try {
|
|
1207
1217
|
const r = await fetch(`${authOrigin}/api/auth/identities`, {
|
|
@@ -1220,10 +1230,10 @@ function OcLinkedIdentities({
|
|
|
1220
1230
|
setError(e instanceof Error ? e.message : "load failed");
|
|
1221
1231
|
}
|
|
1222
1232
|
}, [authOrigin]);
|
|
1223
|
-
|
|
1233
|
+
React4__namespace.useEffect(() => {
|
|
1224
1234
|
void refresh();
|
|
1225
1235
|
}, [refresh]);
|
|
1226
|
-
const afterChange =
|
|
1236
|
+
const afterChange = React4__namespace.useCallback(() => {
|
|
1227
1237
|
setShowEmailLink(false);
|
|
1228
1238
|
setShowBtcLink(false);
|
|
1229
1239
|
void refresh();
|
|
@@ -1255,11 +1265,7 @@ function OcLinkedIdentities({
|
|
|
1255
1265
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className, "data-oc-li": "", style: { display: "grid", gap: 16 }, children: [
|
|
1256
1266
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: panelStyle(), children: [
|
|
1257
1267
|
/* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { children: "\xA7 what this is" }),
|
|
1258
|
-
/* @__PURE__ */ jsxRuntime.
|
|
1259
|
-
"Your public identity is the opaque ",
|
|
1260
|
-
/* @__PURE__ */ jsxRuntime.jsx("code", { style: codeStyle, children: "did:oc:" }),
|
|
1261
|
-
" 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."
|
|
1262
|
-
] })
|
|
1268
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { style: bodyStyle, children: "These are the ways you can sign in to this account. An app you sign in to sees one only if you grant it. Linking a second one is also your way back in: lose one, and the other still signs you in." })
|
|
1263
1269
|
] }),
|
|
1264
1270
|
mergedFrom.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(ConsolidationPanel, { entries: mergedFrom }),
|
|
1265
1271
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: panelStyle("plain"), "data-oc-li-rows": "", children: [
|
|
@@ -1312,7 +1318,7 @@ function OcLinkedIdentities({
|
|
|
1312
1318
|
onCancel: () => setShowBtcLink(false)
|
|
1313
1319
|
}
|
|
1314
1320
|
),
|
|
1315
|
-
hasBtc && /* @__PURE__ */ jsxRuntime.jsx(AttestationLink, {})
|
|
1321
|
+
hasBtc && attestationLink && /* @__PURE__ */ jsxRuntime.jsx(AttestationLink, {})
|
|
1316
1322
|
] });
|
|
1317
1323
|
}
|
|
1318
1324
|
function IdentityGroup({
|
|
@@ -1356,10 +1362,10 @@ function IdentityRow({
|
|
|
1356
1362
|
authOrigin,
|
|
1357
1363
|
onChange
|
|
1358
1364
|
}) {
|
|
1359
|
-
const [shown, setShown] =
|
|
1360
|
-
const [confirming, setConfirming] =
|
|
1361
|
-
const [busy, setBusy] =
|
|
1362
|
-
const [err, setErr] =
|
|
1365
|
+
const [shown, setShown] = React4__namespace.useState(false);
|
|
1366
|
+
const [confirming, setConfirming] = React4__namespace.useState(false);
|
|
1367
|
+
const [busy, setBusy] = React4__namespace.useState(false);
|
|
1368
|
+
const [err, setErr] = React4__namespace.useState(null);
|
|
1363
1369
|
const masked = row.value ? mask(row.value, kind) : "\u2014 pending recovery \u2014";
|
|
1364
1370
|
const display = shown && row.value ? row.value : masked;
|
|
1365
1371
|
async function unlink() {
|
|
@@ -1397,8 +1403,8 @@ function IdentityRow({
|
|
|
1397
1403
|
style: {
|
|
1398
1404
|
marginTop: 4,
|
|
1399
1405
|
color: V.foreground,
|
|
1400
|
-
fontFamily:
|
|
1401
|
-
fontSize: 12,
|
|
1406
|
+
fontFamily: SIGNIN_FONT,
|
|
1407
|
+
fontSize: signinSize(12),
|
|
1402
1408
|
wordBreak: "break-all"
|
|
1403
1409
|
},
|
|
1404
1410
|
children: display
|
|
@@ -1476,8 +1482,8 @@ function ConsolidationPanel({ entries }) {
|
|
|
1476
1482
|
gap: 8,
|
|
1477
1483
|
padding: "8px 0",
|
|
1478
1484
|
borderTop: `1px solid ${V.border}`,
|
|
1479
|
-
fontFamily:
|
|
1480
|
-
fontSize: 11
|
|
1485
|
+
fontFamily: SIGNIN_FONT,
|
|
1486
|
+
fontSize: signinSize(11)
|
|
1481
1487
|
},
|
|
1482
1488
|
children: [
|
|
1483
1489
|
/* @__PURE__ */ jsxRuntime.jsx("code", { style: { color: V.foreground, wordBreak: "break-all" }, children: e.did_oc }),
|
|
@@ -1558,13 +1564,13 @@ function EmailLinkForm({
|
|
|
1558
1564
|
onDone,
|
|
1559
1565
|
onCancel
|
|
1560
1566
|
}) {
|
|
1561
|
-
const [step, setStep] =
|
|
1562
|
-
const [email, setEmail] =
|
|
1563
|
-
const [code, setCode] =
|
|
1564
|
-
const [token, setToken] =
|
|
1565
|
-
const [busy, setBusy] =
|
|
1566
|
-
const [err, setErr] =
|
|
1567
|
-
const [transfer, setTransfer] =
|
|
1567
|
+
const [step, setStep] = React4__namespace.useState("email");
|
|
1568
|
+
const [email, setEmail] = React4__namespace.useState("");
|
|
1569
|
+
const [code, setCode] = React4__namespace.useState("");
|
|
1570
|
+
const [token, setToken] = React4__namespace.useState("");
|
|
1571
|
+
const [busy, setBusy] = React4__namespace.useState(false);
|
|
1572
|
+
const [err, setErr] = React4__namespace.useState(null);
|
|
1573
|
+
const [transfer, setTransfer] = React4__namespace.useState(false);
|
|
1568
1574
|
async function start() {
|
|
1569
1575
|
setBusy(true);
|
|
1570
1576
|
setErr(null);
|
|
@@ -1707,12 +1713,12 @@ function BtcLinkForm({
|
|
|
1707
1713
|
onDone,
|
|
1708
1714
|
onCancel
|
|
1709
1715
|
}) {
|
|
1710
|
-
const [step, setStep] =
|
|
1711
|
-
const [addr, setAddr] =
|
|
1712
|
-
const [challenge, setChallenge] =
|
|
1713
|
-
const [busy, setBusy] =
|
|
1714
|
-
const [err, setErr] =
|
|
1715
|
-
const [pendingSig, setPendingSig] =
|
|
1716
|
+
const [step, setStep] = React4__namespace.useState("request");
|
|
1717
|
+
const [addr, setAddr] = React4__namespace.useState("");
|
|
1718
|
+
const [challenge, setChallenge] = React4__namespace.useState("");
|
|
1719
|
+
const [busy, setBusy] = React4__namespace.useState(false);
|
|
1720
|
+
const [err, setErr] = React4__namespace.useState(null);
|
|
1721
|
+
const [pendingSig, setPendingSig] = React4__namespace.useState(null);
|
|
1716
1722
|
async function getChallenge() {
|
|
1717
1723
|
if (!didOc) {
|
|
1718
1724
|
setErr("session not loaded yet \xB7 refresh and retry");
|
|
@@ -1780,7 +1786,7 @@ function BtcLinkForm({
|
|
|
1780
1786
|
placeholder: "bc1q\u2026",
|
|
1781
1787
|
autoCapitalize: "none",
|
|
1782
1788
|
spellCheck: false,
|
|
1783
|
-
style: { ...inputStyle, fontFamily:
|
|
1789
|
+
style: { ...inputStyle, fontFamily: SIGNIN_FONT }
|
|
1784
1790
|
}
|
|
1785
1791
|
),
|
|
1786
1792
|
err && /* @__PURE__ */ jsxRuntime.jsx(ErrorLine, { children: err }),
|
|
@@ -1810,7 +1816,7 @@ function BtcLinkForm({
|
|
|
1810
1816
|
/* @__PURE__ */ jsxRuntime.jsxs("p", { style: bodyStyle, children: [
|
|
1811
1817
|
"Sign the challenge with the wallet that controls",
|
|
1812
1818
|
" ",
|
|
1813
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontFamily:
|
|
1819
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontFamily: SIGNIN_FONT, wordBreak: "break-all" }, children: addr }),
|
|
1814
1820
|
"."
|
|
1815
1821
|
] }),
|
|
1816
1822
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -1872,9 +1878,9 @@ function TransferConfirm({
|
|
|
1872
1878
|
] });
|
|
1873
1879
|
}
|
|
1874
1880
|
function LazyWalletButton(props) {
|
|
1875
|
-
const [Comp, setComp] =
|
|
1876
|
-
const [failed, setFailed] =
|
|
1877
|
-
|
|
1881
|
+
const [Comp, setComp] = React4__namespace.useState(null);
|
|
1882
|
+
const [failed, setFailed] = React4__namespace.useState(false);
|
|
1883
|
+
React4__namespace.useEffect(() => {
|
|
1878
1884
|
let alive = true;
|
|
1879
1885
|
import('@orangecheck/wallet-adapter/react').then((m) => {
|
|
1880
1886
|
if (alive) {
|
|
@@ -1918,10 +1924,10 @@ function SectionLabel({
|
|
|
1918
1924
|
{
|
|
1919
1925
|
style: {
|
|
1920
1926
|
color,
|
|
1921
|
-
fontFamily:
|
|
1922
|
-
fontSize: 10,
|
|
1923
|
-
letterSpacing: "0.18em",
|
|
1924
|
-
textTransform:
|
|
1927
|
+
fontFamily: SIGNIN_FONT,
|
|
1928
|
+
fontSize: signinSize(10),
|
|
1929
|
+
letterSpacing: signinTracking("0.18em"),
|
|
1930
|
+
textTransform: SIGNIN_CASE,
|
|
1925
1931
|
marginBottom: 8
|
|
1926
1932
|
},
|
|
1927
1933
|
children
|
|
@@ -1936,8 +1942,8 @@ function ErrorLine({ children }) {
|
|
|
1936
1942
|
style: {
|
|
1937
1943
|
margin: "8px 0 0",
|
|
1938
1944
|
color: V.destructive,
|
|
1939
|
-
fontFamily:
|
|
1940
|
-
fontSize: 11
|
|
1945
|
+
fontFamily: SIGNIN_FONT,
|
|
1946
|
+
fontSize: signinSize(11)
|
|
1941
1947
|
},
|
|
1942
1948
|
children
|
|
1943
1949
|
}
|
|
@@ -1946,7 +1952,6 @@ function ErrorLine({ children }) {
|
|
|
1946
1952
|
function FormButtons({ children }) {
|
|
1947
1953
|
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { display: "flex", flexWrap: "wrap", gap: 8, marginTop: 12 }, children });
|
|
1948
1954
|
}
|
|
1949
|
-
var MONO = "ui-monospace, SFMono-Regular, monospace";
|
|
1950
1955
|
var V = {
|
|
1951
1956
|
primary: "var(--primary, #f97316)",
|
|
1952
1957
|
foreground: "var(--foreground, #fafafa)",
|
|
@@ -1978,14 +1983,10 @@ var bodyStyle = {
|
|
|
1978
1983
|
};
|
|
1979
1984
|
var hintStyle = {
|
|
1980
1985
|
color: V.muted,
|
|
1981
|
-
fontFamily:
|
|
1982
|
-
fontSize: 10.5,
|
|
1986
|
+
fontFamily: SIGNIN_FONT,
|
|
1987
|
+
fontSize: signinSize(10.5),
|
|
1983
1988
|
opacity: 0.8
|
|
1984
1989
|
};
|
|
1985
|
-
var codeStyle = {
|
|
1986
|
-
fontFamily: MONO,
|
|
1987
|
-
color: V.foreground
|
|
1988
|
-
};
|
|
1989
1990
|
var rowStyle = {
|
|
1990
1991
|
display: "flex",
|
|
1991
1992
|
flexWrap: "wrap",
|
|
@@ -1996,10 +1997,10 @@ var rowStyle = {
|
|
|
1996
1997
|
};
|
|
1997
1998
|
var rowLabelStyle = {
|
|
1998
1999
|
color: V.foreground,
|
|
1999
|
-
fontFamily:
|
|
2000
|
-
fontSize: 10.5,
|
|
2001
|
-
letterSpacing: "0.12em",
|
|
2002
|
-
textTransform:
|
|
2000
|
+
fontFamily: SIGNIN_FONT,
|
|
2001
|
+
fontSize: signinSize(10.5),
|
|
2002
|
+
letterSpacing: signinTracking("0.12em"),
|
|
2003
|
+
textTransform: SIGNIN_CASE,
|
|
2003
2004
|
opacity: 0.8
|
|
2004
2005
|
};
|
|
2005
2006
|
var inputStyle = {
|
|
@@ -2008,8 +2009,8 @@ var inputStyle = {
|
|
|
2008
2009
|
background: V.background,
|
|
2009
2010
|
color: V.foreground,
|
|
2010
2011
|
border: `1px solid ${V.border}`,
|
|
2011
|
-
borderRadius: 6,
|
|
2012
|
-
fontFamily:
|
|
2012
|
+
borderRadius: signinRadius(6),
|
|
2013
|
+
fontFamily: SIGNIN_FONT,
|
|
2013
2014
|
// 16px — never lower · suppresses iOS Safari focus auto-zoom.
|
|
2014
2015
|
fontSize: 16,
|
|
2015
2016
|
outline: "none",
|
|
@@ -2018,11 +2019,11 @@ var inputStyle = {
|
|
|
2018
2019
|
function btnBase(disabled) {
|
|
2019
2020
|
return {
|
|
2020
2021
|
padding: "0.5rem 0.875rem",
|
|
2021
|
-
borderRadius:
|
|
2022
|
-
fontFamily:
|
|
2023
|
-
fontSize: 10.5,
|
|
2024
|
-
letterSpacing: "0.12em",
|
|
2025
|
-
textTransform:
|
|
2022
|
+
borderRadius: SIGNIN_BUTTON_RADIUS,
|
|
2023
|
+
fontFamily: SIGNIN_FONT,
|
|
2024
|
+
fontSize: signinSize(10.5),
|
|
2025
|
+
letterSpacing: signinTracking("0.12em"),
|
|
2026
|
+
textTransform: SIGNIN_CASE,
|
|
2026
2027
|
cursor: disabled ? "not-allowed" : "pointer",
|
|
2027
2028
|
opacity: disabled ? 0.5 : 1
|
|
2028
2029
|
};
|
|
@@ -2092,6 +2093,50 @@ function hardNavigate(target) {
|
|
|
2092
2093
|
if (typeof window === "undefined") return;
|
|
2093
2094
|
window.location.assign(target);
|
|
2094
2095
|
}
|
|
2096
|
+
var SIGNIN_ERRORS = {
|
|
2097
|
+
bad_request: "That doesn't look like a Bitcoin address.",
|
|
2098
|
+
invalid_challenge: "That signature doesn't match this address. Sign with the wallet that holds it.",
|
|
2099
|
+
sig_invalid: "That signature doesn't match this address. Sign with the wallet that holds it.",
|
|
2100
|
+
sig_unsupported_scheme: "We can't check this wallet's signature format yet. Try another wallet, or use email.",
|
|
2101
|
+
expired: "That request timed out. Try again for a fresh one.",
|
|
2102
|
+
not_yet_valid: "That request is not valid yet. Check your device clock, then try again.",
|
|
2103
|
+
nonce_mismatch: "That request didn't match. Try again for a fresh one.",
|
|
2104
|
+
audience_mismatch: "That request didn't match this site. Reload the page and try again.",
|
|
2105
|
+
purpose_mismatch: "That request didn't match. Try again for a fresh one.",
|
|
2106
|
+
malformed: "That signature couldn't be read. Copy it again, whole.",
|
|
2107
|
+
cross_site_blocked: "Your browser blocked this request. Reload the page and try again.",
|
|
2108
|
+
rate_limited: "Too many tries. Wait a minute, then try again.",
|
|
2109
|
+
invalid_email: "That email address doesn't look right.",
|
|
2110
|
+
mailer_unavailable: "We couldn't send the email right now. Try again in a minute.",
|
|
2111
|
+
mailer_error: "We couldn't send the email right now. Try again in a minute.",
|
|
2112
|
+
wrong_code: "That code isn't right. Check the newest email from us.",
|
|
2113
|
+
replay: "That code was already used. Send a new one.",
|
|
2114
|
+
invalid_token: "That code is no longer valid. Send a new one.",
|
|
2115
|
+
email_mismatch: "That code was sent to a different address. Send a new one."
|
|
2116
|
+
};
|
|
2117
|
+
var EMAIL_ERRORS = {
|
|
2118
|
+
expired: "That code expired. Send a new one."
|
|
2119
|
+
};
|
|
2120
|
+
var SUBMIT_ERRORS = {
|
|
2121
|
+
bad_request: "That signature couldn't be read. Copy it again, whole."
|
|
2122
|
+
};
|
|
2123
|
+
var NETWORK_ERROR = "We couldn't reach the server. Check your connection and try again.";
|
|
2124
|
+
function humanSigninError(reason, path, step = "challenge") {
|
|
2125
|
+
if (!/^[a-z0-9_]+$/.test(reason)) return reason;
|
|
2126
|
+
if (path === "wallet" && step === "submit" && SUBMIT_ERRORS[reason]) return SUBMIT_ERRORS[reason];
|
|
2127
|
+
if (path === "email" && EMAIL_ERRORS[reason]) return EMAIL_ERRORS[reason];
|
|
2128
|
+
return SIGNIN_ERRORS[reason] ?? (path === "email" ? "That didn't work. Try again, or send a new code." : "That didn't work. Try again.");
|
|
2129
|
+
}
|
|
2130
|
+
function failureMessage(err, path, step) {
|
|
2131
|
+
if (err instanceof TypeError || err instanceof SyntaxError) return NETWORK_ERROR;
|
|
2132
|
+
return humanSigninError(err instanceof Error ? err.message : "failed", path, step);
|
|
2133
|
+
}
|
|
2134
|
+
var BECH32_ADDRESS = /^(bc1|tb1|bcrt1)[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{8,87}$/i;
|
|
2135
|
+
var BASE58_ADDRESS = /^[123mn][1-9A-HJ-NP-Za-km-z]{25,34}$/;
|
|
2136
|
+
function looksLikeBitcoinAddress(input) {
|
|
2137
|
+
const s = input.trim();
|
|
2138
|
+
return BECH32_ADDRESS.test(s) || BASE58_ADDRESS.test(s);
|
|
2139
|
+
}
|
|
2095
2140
|
function OcSignIn({
|
|
2096
2141
|
audience,
|
|
2097
2142
|
returnTo,
|
|
@@ -2107,10 +2152,10 @@ function OcSignIn({
|
|
|
2107
2152
|
}) {
|
|
2108
2153
|
const walletEnabled = paths?.wallet ?? true;
|
|
2109
2154
|
const emailEnabled = paths?.email ?? true;
|
|
2110
|
-
const [resolvedReturn, setResolvedReturn] =
|
|
2155
|
+
const [resolvedReturn, setResolvedReturn] = React4__namespace.useState(
|
|
2111
2156
|
() => familyReturnTarget(returnTo) ?? safeReturnTo(returnTo)
|
|
2112
2157
|
);
|
|
2113
|
-
|
|
2158
|
+
React4__namespace.useEffect(() => {
|
|
2114
2159
|
const fromProp = familyReturnTarget(returnTo);
|
|
2115
2160
|
if (fromProp) {
|
|
2116
2161
|
setResolvedReturn(fromProp);
|
|
@@ -2121,8 +2166,8 @@ function OcSignIn({
|
|
|
2121
2166
|
const fromQuery = familyReturnTarget(q.get("return_to")) ?? familyReturnTarget(q.get("next"));
|
|
2122
2167
|
setResolvedReturn(fromQuery ?? "/");
|
|
2123
2168
|
}, [returnTo]);
|
|
2124
|
-
const [addMode, setAddMode] =
|
|
2125
|
-
|
|
2169
|
+
const [addMode, setAddMode] = React4__namespace.useState(Boolean(addProp));
|
|
2170
|
+
React4__namespace.useEffect(() => {
|
|
2126
2171
|
if (typeof window === "undefined") return;
|
|
2127
2172
|
if (addProp !== void 0) {
|
|
2128
2173
|
setAddMode(Boolean(addProp));
|
|
@@ -2130,19 +2175,19 @@ function OcSignIn({
|
|
|
2130
2175
|
}
|
|
2131
2176
|
setAddMode(new URLSearchParams(window.location.search).get("add") === "1");
|
|
2132
2177
|
}, [addProp]);
|
|
2133
|
-
const [path, setPath] =
|
|
2178
|
+
const [path, setPath] = React4__namespace.useState(
|
|
2134
2179
|
initialPath ?? (providersFirst ? "email" : "wallet")
|
|
2135
2180
|
);
|
|
2136
|
-
const [signedIn, setSignedIn] =
|
|
2137
|
-
const [linkAlso, setLinkAlso] =
|
|
2138
|
-
const [oauthError, setOauthError] =
|
|
2139
|
-
|
|
2181
|
+
const [signedIn, setSignedIn] = React4__namespace.useState(null);
|
|
2182
|
+
const [linkAlso, setLinkAlso] = React4__namespace.useState(false);
|
|
2183
|
+
const [oauthError, setOauthError] = React4__namespace.useState(false);
|
|
2184
|
+
React4__namespace.useEffect(() => {
|
|
2140
2185
|
if (typeof window === "undefined") return;
|
|
2141
2186
|
if (new URLSearchParams(window.location.search).has("oauth_error")) {
|
|
2142
2187
|
setOauthError(true);
|
|
2143
2188
|
}
|
|
2144
2189
|
}, []);
|
|
2145
|
-
const navigate =
|
|
2190
|
+
const navigate = React4__namespace.useCallback(
|
|
2146
2191
|
async (account) => {
|
|
2147
2192
|
if (resolveReturnTo) {
|
|
2148
2193
|
try {
|
|
@@ -2156,7 +2201,7 @@ function OcSignIn({
|
|
|
2156
2201
|
},
|
|
2157
2202
|
[resolveReturnTo, resolvedReturn]
|
|
2158
2203
|
);
|
|
2159
|
-
const handleSuccess =
|
|
2204
|
+
const handleSuccess = React4__namespace.useCallback(
|
|
2160
2205
|
async (account, token, via) => {
|
|
2161
2206
|
const proceed = () => {
|
|
2162
2207
|
clearTabSession();
|
|
@@ -2198,8 +2243,8 @@ function OcSignIn({
|
|
|
2198
2243
|
padding: "1rem",
|
|
2199
2244
|
border: "1px solid var(--border, #27272a)",
|
|
2200
2245
|
color: "var(--foreground, #fafafa)",
|
|
2201
|
-
fontFamily:
|
|
2202
|
-
fontSize: 12
|
|
2246
|
+
fontFamily: SIGNIN_FONT,
|
|
2247
|
+
fontSize: signinSize(12)
|
|
2203
2248
|
},
|
|
2204
2249
|
children: [
|
|
2205
2250
|
"no signin paths enabled \u2014 set ",
|
|
@@ -2237,12 +2282,12 @@ function OcSignIn({
|
|
|
2237
2282
|
marginBottom: 14,
|
|
2238
2283
|
padding: "0.6rem 0.75rem",
|
|
2239
2284
|
border: "1px solid var(--destructive, #ef4444)",
|
|
2240
|
-
borderRadius: 6,
|
|
2285
|
+
borderRadius: signinRadius(6),
|
|
2241
2286
|
color: "var(--destructive, #ef4444)",
|
|
2242
|
-
fontFamily:
|
|
2243
|
-
fontSize: 11
|
|
2287
|
+
fontFamily: SIGNIN_FONT,
|
|
2288
|
+
fontSize: signinSize(11)
|
|
2244
2289
|
},
|
|
2245
|
-
children: "That sign-in didn't
|
|
2290
|
+
children: "That sign-in didn't finish. Try again, or use another way below."
|
|
2246
2291
|
}
|
|
2247
2292
|
),
|
|
2248
2293
|
addMode && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
@@ -2254,11 +2299,11 @@ function OcSignIn({
|
|
|
2254
2299
|
padding: "0.6rem 0.75rem",
|
|
2255
2300
|
border: "1px solid var(--primary, #f97316)",
|
|
2256
2301
|
borderLeftWidth: 3,
|
|
2257
|
-
borderRadius: 4,
|
|
2302
|
+
borderRadius: signinRadius(4),
|
|
2258
2303
|
background: "color-mix(in srgb, var(--primary, #f97316) 8%, transparent)",
|
|
2259
2304
|
color: "var(--foreground, #fafafa)",
|
|
2260
|
-
fontFamily:
|
|
2261
|
-
fontSize: 11,
|
|
2305
|
+
fontFamily: SIGNIN_FONT,
|
|
2306
|
+
fontSize: signinSize(11),
|
|
2262
2307
|
lineHeight: 1.6
|
|
2263
2308
|
},
|
|
2264
2309
|
children: [
|
|
@@ -2267,9 +2312,9 @@ function OcSignIn({
|
|
|
2267
2312
|
{
|
|
2268
2313
|
style: {
|
|
2269
2314
|
color: "var(--primary, #f97316)",
|
|
2270
|
-
letterSpacing: "0.12em",
|
|
2271
|
-
textTransform:
|
|
2272
|
-
fontSize: 10,
|
|
2315
|
+
letterSpacing: signinTracking("0.12em"),
|
|
2316
|
+
textTransform: SIGNIN_CASE,
|
|
2317
|
+
fontSize: signinSize(10),
|
|
2273
2318
|
display: "block",
|
|
2274
2319
|
marginBottom: 3
|
|
2275
2320
|
},
|
|
@@ -2307,7 +2352,7 @@ function OcSignIn({
|
|
|
2307
2352
|
{
|
|
2308
2353
|
active: path === "email",
|
|
2309
2354
|
onClick: () => setPath("email"),
|
|
2310
|
-
children: "email
|
|
2355
|
+
children: "email"
|
|
2311
2356
|
}
|
|
2312
2357
|
),
|
|
2313
2358
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -2315,7 +2360,7 @@ function OcSignIn({
|
|
|
2315
2360
|
{
|
|
2316
2361
|
active: path === "wallet",
|
|
2317
2362
|
onClick: () => setPath("wallet"),
|
|
2318
|
-
children: "bitcoin
|
|
2363
|
+
children: "bitcoin wallet"
|
|
2319
2364
|
}
|
|
2320
2365
|
)
|
|
2321
2366
|
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
@@ -2332,7 +2377,7 @@ function OcSignIn({
|
|
|
2332
2377
|
{
|
|
2333
2378
|
active: path === "email",
|
|
2334
2379
|
onClick: () => setPath("email"),
|
|
2335
|
-
children: "email
|
|
2380
|
+
children: "email"
|
|
2336
2381
|
}
|
|
2337
2382
|
)
|
|
2338
2383
|
] })
|
|
@@ -2365,7 +2410,7 @@ function OcSignIn({
|
|
|
2365
2410
|
type: "checkbox",
|
|
2366
2411
|
checked: linkAlso,
|
|
2367
2412
|
onChange: (e) => setLinkAlso(e.target.checked),
|
|
2368
|
-
style: {
|
|
2413
|
+
style: { margin: 0, accentColor: "var(--primary, #f97316)" }
|
|
2369
2414
|
}
|
|
2370
2415
|
),
|
|
2371
2416
|
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
@@ -2373,7 +2418,7 @@ function OcSignIn({
|
|
|
2373
2418
|
" ",
|
|
2374
2419
|
activeMethod === "email" ? "a Bitcoin wallet" : "an email",
|
|
2375
2420
|
" ",
|
|
2376
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", {
|
|
2421
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "\u2014 optional, one more signature." })
|
|
2377
2422
|
] })
|
|
2378
2423
|
] })
|
|
2379
2424
|
] });
|
|
@@ -2392,15 +2437,16 @@ function SigninTab({
|
|
|
2392
2437
|
onClick,
|
|
2393
2438
|
"data-oc-signin-tab": active ? "active" : "inactive",
|
|
2394
2439
|
style: {
|
|
2440
|
+
minHeight: 44,
|
|
2395
2441
|
padding: "0.6rem 0.875rem",
|
|
2396
2442
|
background: "transparent",
|
|
2397
2443
|
border: "none",
|
|
2398
2444
|
borderBottom: active ? "2px solid var(--primary, #f97316)" : "2px solid transparent",
|
|
2399
2445
|
color: active ? "var(--foreground, #fafafa)" : "var(--muted-foreground, #a1a1aa)",
|
|
2400
|
-
fontFamily:
|
|
2401
|
-
fontSize: 11,
|
|
2402
|
-
letterSpacing: "0.12em",
|
|
2403
|
-
textTransform:
|
|
2446
|
+
fontFamily: SIGNIN_FONT,
|
|
2447
|
+
fontSize: signinSize(11),
|
|
2448
|
+
letterSpacing: signinTracking("0.12em"),
|
|
2449
|
+
textTransform: SIGNIN_CASE,
|
|
2404
2450
|
cursor: "pointer",
|
|
2405
2451
|
marginBottom: -1
|
|
2406
2452
|
},
|
|
@@ -2431,20 +2477,21 @@ function ProviderSignIn({
|
|
|
2431
2477
|
add,
|
|
2432
2478
|
first = false
|
|
2433
2479
|
}) {
|
|
2434
|
-
const [providers, setProviders] =
|
|
2435
|
-
const [origin, setOrigin] =
|
|
2436
|
-
|
|
2480
|
+
const [providers, setProviders] = React4__namespace.useState(null);
|
|
2481
|
+
const [origin, setOrigin] = React4__namespace.useState("");
|
|
2482
|
+
React4__namespace.useEffect(() => {
|
|
2437
2483
|
setOrigin(window.location.origin);
|
|
2438
2484
|
let cancelled = false;
|
|
2439
2485
|
fetch(`${authOrigin}/api/auth/providers`, { credentials: "include" }).then((r) => r.ok ? r.json() : null).then((body) => {
|
|
2440
|
-
if (!cancelled
|
|
2486
|
+
if (!cancelled) setProviders(body?.providers ?? []);
|
|
2441
2487
|
}).catch(() => {
|
|
2488
|
+
if (!cancelled) setProviders([]);
|
|
2442
2489
|
});
|
|
2443
2490
|
return () => {
|
|
2444
2491
|
cancelled = true;
|
|
2445
2492
|
};
|
|
2446
2493
|
}, [authOrigin]);
|
|
2447
|
-
if (providers.length === 0) return null;
|
|
2494
|
+
if (providers !== null && providers.length === 0) return null;
|
|
2448
2495
|
const providerReturnTo = returnTo.startsWith("/") ? origin ? `${origin}${returnTo}` : returnTo : returnTo;
|
|
2449
2496
|
const line = { flex: 1, height: 1, background: "var(--border, #27272a)" };
|
|
2450
2497
|
const BLOCK_GAP = 16;
|
|
@@ -2461,10 +2508,10 @@ function ProviderSignIn({
|
|
|
2461
2508
|
// marginTop owns the gap above, so the divider only pads below.
|
|
2462
2509
|
margin: first ? `${BLOCK_GAP}px 0` : `0 0 ${BLOCK_GAP}px`,
|
|
2463
2510
|
color: "var(--muted-foreground, #a1a1aa)",
|
|
2464
|
-
fontFamily:
|
|
2465
|
-
fontSize: 10,
|
|
2466
|
-
letterSpacing: "0.16em",
|
|
2467
|
-
textTransform:
|
|
2511
|
+
fontFamily: SIGNIN_FONT,
|
|
2512
|
+
fontSize: signinSize(10),
|
|
2513
|
+
letterSpacing: signinTracking("0.16em"),
|
|
2514
|
+
textTransform: SIGNIN_CASE
|
|
2468
2515
|
},
|
|
2469
2516
|
children: [
|
|
2470
2517
|
/* @__PURE__ */ jsxRuntime.jsx("span", { style: line }),
|
|
@@ -2473,6 +2520,31 @@ function ProviderSignIn({
|
|
|
2473
2520
|
]
|
|
2474
2521
|
}
|
|
2475
2522
|
);
|
|
2523
|
+
const buttonStyle = (i) => ({
|
|
2524
|
+
display: "flex",
|
|
2525
|
+
alignItems: "center",
|
|
2526
|
+
justifyContent: "center",
|
|
2527
|
+
gap: 10,
|
|
2528
|
+
boxSizing: "border-box",
|
|
2529
|
+
width: "100%",
|
|
2530
|
+
minHeight: 44,
|
|
2531
|
+
marginTop: i === 0 ? 0 : BUTTON_GAP,
|
|
2532
|
+
padding: "0.6rem 0.875rem",
|
|
2533
|
+
border: "1px solid var(--border, #27272a)",
|
|
2534
|
+
borderRadius: SIGNIN_BUTTON_RADIUS,
|
|
2535
|
+
background: "transparent",
|
|
2536
|
+
color: "var(--muted-foreground, #a1a1aa)",
|
|
2537
|
+
fontFamily: SIGNIN_FONT,
|
|
2538
|
+
fontSize: signinSize(12),
|
|
2539
|
+
textDecoration: "none"
|
|
2540
|
+
});
|
|
2541
|
+
if (providers === null) {
|
|
2542
|
+
if (!first) return null;
|
|
2543
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-oc-signin-providers": "", "aria-hidden": "true", children: [
|
|
2544
|
+
[0, 1].map((i) => /* @__PURE__ */ jsxRuntime.jsx("div", { style: { ...buttonStyle(i), visibility: "hidden" }, children: "\xA0" }, i)),
|
|
2545
|
+
divider
|
|
2546
|
+
] });
|
|
2547
|
+
}
|
|
2476
2548
|
const buttons = providers.map((p, i) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2477
2549
|
"a",
|
|
2478
2550
|
{
|
|
@@ -2480,23 +2552,7 @@ function ProviderSignIn({
|
|
|
2480
2552
|
providerReturnTo
|
|
2481
2553
|
)}${add ? "&add=1" : ""}`,
|
|
2482
2554
|
"data-oc-signin-provider": p.id,
|
|
2483
|
-
style:
|
|
2484
|
-
display: "flex",
|
|
2485
|
-
alignItems: "center",
|
|
2486
|
-
justifyContent: "center",
|
|
2487
|
-
gap: 10,
|
|
2488
|
-
boxSizing: "border-box",
|
|
2489
|
-
width: "100%",
|
|
2490
|
-
marginTop: i === 0 ? 0 : BUTTON_GAP,
|
|
2491
|
-
padding: "0.6rem 0.875rem",
|
|
2492
|
-
border: "1px solid var(--border, #27272a)",
|
|
2493
|
-
borderRadius: 6,
|
|
2494
|
-
background: "transparent",
|
|
2495
|
-
color: "var(--muted-foreground, #a1a1aa)",
|
|
2496
|
-
fontFamily: "ui-monospace, SFMono-Regular, monospace",
|
|
2497
|
-
fontSize: 12,
|
|
2498
|
-
textDecoration: "none"
|
|
2499
|
-
},
|
|
2555
|
+
style: buttonStyle(i),
|
|
2500
2556
|
children: [
|
|
2501
2557
|
/* @__PURE__ */ jsxRuntime.jsx(ProviderIcon, { id: p.id }),
|
|
2502
2558
|
/* @__PURE__ */ jsxRuntime.jsx("span", { children: p.label })
|
|
@@ -2519,82 +2575,214 @@ function ProviderSignIn({
|
|
|
2519
2575
|
}
|
|
2520
2576
|
);
|
|
2521
2577
|
}
|
|
2578
|
+
async function loadAdapter() {
|
|
2579
|
+
return await import('@orangecheck/wallet-adapter');
|
|
2580
|
+
}
|
|
2581
|
+
async function detectBrowserWallets() {
|
|
2582
|
+
return (await loadAdapter()).detectWallets().filter((w) => w.detected && w.id !== "manual").map((w) => ({ id: w.id, name: w.name }));
|
|
2583
|
+
}
|
|
2522
2584
|
function WalletFlow({ authOrigin, audience, add, onSuccess }) {
|
|
2523
|
-
const
|
|
2524
|
-
const
|
|
2525
|
-
|
|
2526
|
-
|
|
2585
|
+
const uid = React4__namespace.useId();
|
|
2586
|
+
const ids = {
|
|
2587
|
+
address: `${uid}-address`,
|
|
2588
|
+
wallet: `${uid}-wallet`,
|
|
2589
|
+
message: `${uid}-message`,
|
|
2590
|
+
signature: `${uid}-signature`
|
|
2591
|
+
};
|
|
2592
|
+
const [address, setAddress] = React4__namespace.useState("");
|
|
2593
|
+
const [error, setError] = React4__namespace.useState(null);
|
|
2594
|
+
const [submitting, setSubmitting] = React4__namespace.useState(false);
|
|
2595
|
+
const [wallets, setWallets] = React4__namespace.useState(null);
|
|
2596
|
+
const [walletId, setWalletId] = React4__namespace.useState(null);
|
|
2597
|
+
const [manual, setManual] = React4__namespace.useState(null);
|
|
2598
|
+
const [pasted, setPasted] = React4__namespace.useState("");
|
|
2599
|
+
const [copied, setCopied] = React4__namespace.useState("idle");
|
|
2600
|
+
React4__namespace.useEffect(() => {
|
|
2601
|
+
let cancelled = false;
|
|
2602
|
+
detectBrowserWallets().then((found) => {
|
|
2603
|
+
if (cancelled) return;
|
|
2604
|
+
setWallets(found);
|
|
2605
|
+
setWalletId(found[0]?.id ?? null);
|
|
2606
|
+
}).catch(() => {
|
|
2607
|
+
if (!cancelled) setWallets([]);
|
|
2608
|
+
});
|
|
2609
|
+
return () => {
|
|
2610
|
+
cancelled = true;
|
|
2611
|
+
};
|
|
2612
|
+
}, []);
|
|
2613
|
+
async function fetchChallenge(addr) {
|
|
2614
|
+
const res = await fetch(
|
|
2615
|
+
`${authOrigin}/api/challenge?addr=${encodeURIComponent(addr)}&audience=${encodeURIComponent(audience)}&purpose=login`
|
|
2616
|
+
);
|
|
2617
|
+
const json = await res.json();
|
|
2618
|
+
if (!res.ok || !json.message || !json.nonce) {
|
|
2619
|
+
throw new Error(json.error ?? "challenge_failed");
|
|
2620
|
+
}
|
|
2621
|
+
return { message: json.message, nonce: json.nonce };
|
|
2622
|
+
}
|
|
2623
|
+
async function submitSignature(message, nonce, signature, scheme) {
|
|
2624
|
+
const res = await fetch(`${authOrigin}/api/auth/signin`, {
|
|
2625
|
+
method: "POST",
|
|
2626
|
+
credentials: "include",
|
|
2627
|
+
headers: { "Content-Type": "application/json" },
|
|
2628
|
+
body: JSON.stringify({
|
|
2629
|
+
message,
|
|
2630
|
+
signature,
|
|
2631
|
+
...scheme ? { scheme } : {},
|
|
2632
|
+
expectedNonce: nonce,
|
|
2633
|
+
expectedAudience: audience,
|
|
2634
|
+
expectedPurpose: "login",
|
|
2635
|
+
// Multi-account add-mode · the auth host preserves the current
|
|
2636
|
+
// roster_id when set, instead of minting a fresh one.
|
|
2637
|
+
...add ? { add: true } : {}
|
|
2638
|
+
})
|
|
2639
|
+
});
|
|
2640
|
+
const json = await res.json();
|
|
2641
|
+
if (!res.ok || !("ok" in json) || !json.ok || !json.account) {
|
|
2642
|
+
throw new Error(
|
|
2643
|
+
"reason" in json && json.reason || "error" in json && json.error || "verify_failed"
|
|
2644
|
+
);
|
|
2645
|
+
}
|
|
2646
|
+
onSuccess(json.account, json.token);
|
|
2647
|
+
}
|
|
2527
2648
|
async function signIn(e) {
|
|
2528
2649
|
e.preventDefault();
|
|
2529
2650
|
const addr = address.trim();
|
|
2530
2651
|
if (!addr) {
|
|
2531
|
-
setError("
|
|
2652
|
+
setError("Paste your Bitcoin address.");
|
|
2653
|
+
return;
|
|
2654
|
+
}
|
|
2655
|
+
if (!looksLikeBitcoinAddress(addr)) {
|
|
2656
|
+
setError(humanSigninError("bad_request", "wallet"));
|
|
2532
2657
|
return;
|
|
2533
2658
|
}
|
|
2534
2659
|
setError(null);
|
|
2535
2660
|
setSubmitting(true);
|
|
2536
|
-
|
|
2661
|
+
let step = "challenge";
|
|
2537
2662
|
try {
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
|
|
2663
|
+
let id = walletId;
|
|
2664
|
+
if (wallets === null) {
|
|
2665
|
+
const found = await detectBrowserWallets().catch(() => []);
|
|
2666
|
+
setWallets(found);
|
|
2667
|
+
id = found[0]?.id ?? null;
|
|
2668
|
+
setWalletId(id);
|
|
2544
2669
|
}
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
"could not load wallet support \xB7 refresh to try again"
|
|
2551
|
-
);
|
|
2552
|
-
}
|
|
2553
|
-
const wallets = adapter.detectWallets().filter((w) => w.detected && w.id !== "manual");
|
|
2554
|
-
if (wallets.length === 0) {
|
|
2555
|
-
throw new Error("no BIP-322 wallet extension detected \xB7 install one and refresh");
|
|
2670
|
+
const challenge = await fetchChallenge(addr);
|
|
2671
|
+
if (!id) {
|
|
2672
|
+
setManual(challenge);
|
|
2673
|
+
setSubmitting(false);
|
|
2674
|
+
return;
|
|
2556
2675
|
}
|
|
2557
|
-
const
|
|
2558
|
-
const signer = adapter.getSigner(wallet.id, { address: addr });
|
|
2676
|
+
const signer = (await loadAdapter()).getSigner(id, { address: addr });
|
|
2559
2677
|
const signature = await signer(challenge.message);
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2565
|
-
|
|
2566
|
-
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
if (!res.ok || !("ok" in json) || !json.ok || !json.account) {
|
|
2580
|
-
throw new Error(
|
|
2581
|
-
"reason" in json && json.reason || "error" in json && json.error || `verify_failed_${res.status}`
|
|
2582
|
-
);
|
|
2583
|
-
}
|
|
2584
|
-
onSuccess(json.account, json.token);
|
|
2678
|
+
step = "submit";
|
|
2679
|
+
await submitSignature(challenge.message, challenge.nonce, signature, "bip322");
|
|
2680
|
+
} catch (err) {
|
|
2681
|
+
setError(failureMessage(err, "wallet", step));
|
|
2682
|
+
setSubmitting(false);
|
|
2683
|
+
}
|
|
2684
|
+
}
|
|
2685
|
+
async function signInManual(e) {
|
|
2686
|
+
e.preventDefault();
|
|
2687
|
+
if (!manual) return;
|
|
2688
|
+
const sig = pasted.trim();
|
|
2689
|
+
if (!sig) {
|
|
2690
|
+
setError("Paste the signature your wallet gave you.");
|
|
2691
|
+
return;
|
|
2692
|
+
}
|
|
2693
|
+
setError(null);
|
|
2694
|
+
setSubmitting(true);
|
|
2695
|
+
try {
|
|
2696
|
+
await submitSignature(manual.message, manual.nonce, sig, void 0);
|
|
2585
2697
|
} catch (err) {
|
|
2586
|
-
|
|
2587
|
-
setError(msg);
|
|
2698
|
+
setError(failureMessage(err, "wallet", "submit"));
|
|
2588
2699
|
setSubmitting(false);
|
|
2589
|
-
setStage("enter");
|
|
2590
2700
|
}
|
|
2591
2701
|
}
|
|
2702
|
+
async function copyMessage() {
|
|
2703
|
+
if (!manual) return;
|
|
2704
|
+
try {
|
|
2705
|
+
await navigator.clipboard.writeText(manual.message);
|
|
2706
|
+
setCopied("copied");
|
|
2707
|
+
} catch {
|
|
2708
|
+
setCopied("failed");
|
|
2709
|
+
}
|
|
2710
|
+
}
|
|
2711
|
+
if (manual) {
|
|
2712
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: signInManual, "data-oc-signin-wallet-manual": "", children: [
|
|
2713
|
+
/* @__PURE__ */ jsxRuntime.jsxs(FlowHeader, { children: [
|
|
2714
|
+
"In your wallet, use \u201Csign message\u201D (BIP-322) with ",
|
|
2715
|
+
address.trim(),
|
|
2716
|
+
", sign the text below, and paste the signature back here. Nothing is spent."
|
|
2717
|
+
] }),
|
|
2718
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: ids.message, children: "message to sign" }),
|
|
2719
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2720
|
+
"textarea",
|
|
2721
|
+
{
|
|
2722
|
+
id: ids.message,
|
|
2723
|
+
readOnly: true,
|
|
2724
|
+
value: manual.message,
|
|
2725
|
+
rows: 5,
|
|
2726
|
+
onFocus: (e) => e.currentTarget.select(),
|
|
2727
|
+
style: { ...inputStyle2, resize: "vertical" }
|
|
2728
|
+
}
|
|
2729
|
+
),
|
|
2730
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", onClick: () => void copyMessage(), style: linkButtonStyle, children: copied === "copied" ? "copied" : "copy message" }),
|
|
2731
|
+
copied === "failed" && /* @__PURE__ */ jsxRuntime.jsx(Hint, { children: "Couldn't copy here. Select the text above and copy it." }),
|
|
2732
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: 14 }, children: [
|
|
2733
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: ids.signature, children: "signature" }),
|
|
2734
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2735
|
+
"textarea",
|
|
2736
|
+
{
|
|
2737
|
+
id: ids.signature,
|
|
2738
|
+
value: pasted,
|
|
2739
|
+
onChange: (e) => {
|
|
2740
|
+
setPasted(e.target.value);
|
|
2741
|
+
if (error) setError(null);
|
|
2742
|
+
},
|
|
2743
|
+
rows: 3,
|
|
2744
|
+
placeholder: "paste the signature",
|
|
2745
|
+
spellCheck: false,
|
|
2746
|
+
autoFocus: true,
|
|
2747
|
+
"aria-invalid": error ? true : void 0,
|
|
2748
|
+
style: { ...inputStyle2, resize: "vertical" }
|
|
2749
|
+
}
|
|
2750
|
+
)
|
|
2751
|
+
] }),
|
|
2752
|
+
error && /* @__PURE__ */ jsxRuntime.jsx(ErrorLine2, { children: error }),
|
|
2753
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2754
|
+
"button",
|
|
2755
|
+
{
|
|
2756
|
+
type: "submit",
|
|
2757
|
+
disabled: submitting || !pasted.trim(),
|
|
2758
|
+
style: submitStyle(submitting || !pasted.trim()),
|
|
2759
|
+
children: submitting ? "checking\u2026" : "sign me in \u2192"
|
|
2760
|
+
}
|
|
2761
|
+
),
|
|
2762
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2763
|
+
"button",
|
|
2764
|
+
{
|
|
2765
|
+
type: "button",
|
|
2766
|
+
onClick: () => {
|
|
2767
|
+
setManual(null);
|
|
2768
|
+
setPasted("");
|
|
2769
|
+
setError(null);
|
|
2770
|
+
setCopied("idle");
|
|
2771
|
+
},
|
|
2772
|
+
style: linkButtonStyle,
|
|
2773
|
+
children: "use a different address"
|
|
2774
|
+
}
|
|
2775
|
+
)
|
|
2776
|
+
] });
|
|
2777
|
+
}
|
|
2778
|
+
const noBrowserWallet = wallets !== null && wallets.length === 0;
|
|
2592
2779
|
return /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: signIn, "data-oc-signin-wallet": "", children: [
|
|
2593
|
-
/* @__PURE__ */ jsxRuntime.jsx(FlowHeader, {
|
|
2594
|
-
/* @__PURE__ */ jsxRuntime.jsx(Label, { children: "bitcoin address" }),
|
|
2780
|
+
/* @__PURE__ */ jsxRuntime.jsx(FlowHeader, { children: "Your wallet signs a short message to prove you hold the address. Nothing is spent and your key never leaves the wallet. Works with browser wallets, or any wallet that can sign a message, like Sparrow." }),
|
|
2781
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: ids.address, children: "bitcoin address" }),
|
|
2595
2782
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2596
2783
|
"input",
|
|
2597
2784
|
{
|
|
2785
|
+
id: ids.address,
|
|
2598
2786
|
type: "text",
|
|
2599
2787
|
value: address,
|
|
2600
2788
|
onChange: (e) => {
|
|
@@ -2610,6 +2798,19 @@ function WalletFlow({ authOrigin, audience, add, onSuccess }) {
|
|
|
2610
2798
|
style: inputStyle2
|
|
2611
2799
|
}
|
|
2612
2800
|
),
|
|
2801
|
+
wallets && wallets.length > 1 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: 12 }, children: [
|
|
2802
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: ids.wallet, children: "sign with" }),
|
|
2803
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2804
|
+
"select",
|
|
2805
|
+
{
|
|
2806
|
+
id: ids.wallet,
|
|
2807
|
+
value: walletId ?? "",
|
|
2808
|
+
onChange: (e) => setWalletId(e.target.value),
|
|
2809
|
+
style: inputStyle2,
|
|
2810
|
+
children: wallets.map((w) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: w.id, children: w.name }, w.id))
|
|
2811
|
+
}
|
|
2812
|
+
)
|
|
2813
|
+
] }),
|
|
2613
2814
|
error && /* @__PURE__ */ jsxRuntime.jsx(ErrorLine2, { children: error }),
|
|
2614
2815
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2615
2816
|
"button",
|
|
@@ -2617,54 +2818,71 @@ function WalletFlow({ authOrigin, audience, add, onSuccess }) {
|
|
|
2617
2818
|
type: "submit",
|
|
2618
2819
|
disabled: submitting || !address.trim(),
|
|
2619
2820
|
style: submitStyle(submitting || !address.trim()),
|
|
2620
|
-
children:
|
|
2821
|
+
children: submitting ? "waiting for wallet\u2026" : noBrowserWallet ? "get the message to sign \u2192" : "sign in with wallet \u2192"
|
|
2621
2822
|
}
|
|
2622
2823
|
),
|
|
2623
|
-
/* @__PURE__ */ jsxRuntime.jsx(Hint, { children: "
|
|
2824
|
+
noBrowserWallet && /* @__PURE__ */ jsxRuntime.jsx(Hint, { children: "No browser wallet found here. You can sign with any wallet and paste." })
|
|
2624
2825
|
] });
|
|
2625
2826
|
}
|
|
2626
2827
|
function EmailFlow({ authOrigin, add, onSuccess }) {
|
|
2627
|
-
const
|
|
2628
|
-
const [
|
|
2629
|
-
const [
|
|
2630
|
-
const [
|
|
2631
|
-
const [
|
|
2632
|
-
const [
|
|
2633
|
-
const [
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
if (!trimmed || !/.+@.+\..+/.test(trimmed)) {
|
|
2638
|
-
setEmailError("enter a valid email");
|
|
2639
|
-
return;
|
|
2640
|
-
}
|
|
2641
|
-
setEmailError(null);
|
|
2642
|
-
setSubmitting(true);
|
|
2828
|
+
const uid = React4__namespace.useId();
|
|
2829
|
+
const [stage, setStage] = React4__namespace.useState("enter");
|
|
2830
|
+
const [email, setEmail] = React4__namespace.useState("");
|
|
2831
|
+
const [emailError, setEmailError] = React4__namespace.useState(null);
|
|
2832
|
+
const [code, setCode] = React4__namespace.useState("");
|
|
2833
|
+
const [codeError, setCodeError] = React4__namespace.useState(null);
|
|
2834
|
+
const [token, setToken] = React4__namespace.useState(null);
|
|
2835
|
+
const [submitting, setSubmitting] = React4__namespace.useState(false);
|
|
2836
|
+
const [resent, setResent] = React4__namespace.useState(false);
|
|
2837
|
+
async function sendCode(address) {
|
|
2643
2838
|
try {
|
|
2644
2839
|
const res = await fetch(`${authOrigin}/api/auth/email-otp/start`, {
|
|
2645
2840
|
method: "POST",
|
|
2646
2841
|
credentials: "include",
|
|
2647
2842
|
headers: { "Content-Type": "application/json" },
|
|
2648
|
-
body: JSON.stringify({ email:
|
|
2843
|
+
body: JSON.stringify({ email: address })
|
|
2649
2844
|
});
|
|
2650
2845
|
const json = await res.json();
|
|
2651
|
-
if (!res.ok || !json.token)
|
|
2652
|
-
throw new Error(json.reason ?? `start failed (${res.status})`);
|
|
2653
|
-
}
|
|
2846
|
+
if (!res.ok || !json.token) return humanSigninError(json.reason ?? "server_error", "email");
|
|
2654
2847
|
setToken(json.token);
|
|
2655
|
-
|
|
2656
|
-
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2848
|
+
return null;
|
|
2849
|
+
} catch {
|
|
2850
|
+
return NETWORK_ERROR;
|
|
2851
|
+
}
|
|
2852
|
+
}
|
|
2853
|
+
async function start(e) {
|
|
2854
|
+
e.preventDefault();
|
|
2855
|
+
const trimmed = email.trim().toLowerCase();
|
|
2856
|
+
if (!trimmed || !/.+@.+\..+/.test(trimmed)) {
|
|
2857
|
+
setEmailError(humanSigninError("invalid_email", "email"));
|
|
2858
|
+
return;
|
|
2661
2859
|
}
|
|
2860
|
+
setEmailError(null);
|
|
2861
|
+
setSubmitting(true);
|
|
2862
|
+
const err = await sendCode(trimmed);
|
|
2863
|
+
setSubmitting(false);
|
|
2864
|
+
if (err) {
|
|
2865
|
+
setEmailError(err);
|
|
2866
|
+
return;
|
|
2867
|
+
}
|
|
2868
|
+
setEmail(trimmed);
|
|
2869
|
+
setStage("code");
|
|
2870
|
+
}
|
|
2871
|
+
async function resend() {
|
|
2872
|
+
setCodeError(null);
|
|
2873
|
+
setResent(false);
|
|
2874
|
+
setSubmitting(true);
|
|
2875
|
+
const err = await sendCode(email);
|
|
2876
|
+
setSubmitting(false);
|
|
2877
|
+
setCode("");
|
|
2878
|
+
if (err) setCodeError(err);
|
|
2879
|
+
else setResent(true);
|
|
2662
2880
|
}
|
|
2663
2881
|
async function verify(e) {
|
|
2664
2882
|
e.preventDefault();
|
|
2665
2883
|
if (!token) return;
|
|
2666
2884
|
if (code.length !== 6) {
|
|
2667
|
-
setCodeError("6 digits");
|
|
2885
|
+
setCodeError("The code is 6 digits.");
|
|
2668
2886
|
return;
|
|
2669
2887
|
}
|
|
2670
2888
|
setCodeError(null);
|
|
@@ -2685,23 +2903,24 @@ function EmailFlow({ authOrigin, add, onSuccess }) {
|
|
|
2685
2903
|
const json = await res.json();
|
|
2686
2904
|
if (!res.ok || !("ok" in json) || !json.ok || !json.account) {
|
|
2687
2905
|
throw new Error(
|
|
2688
|
-
"reason" in json && json.reason || "error" in json && json.error ||
|
|
2906
|
+
"reason" in json && json.reason || "error" in json && json.error || "server_error"
|
|
2689
2907
|
);
|
|
2690
2908
|
}
|
|
2691
2909
|
onSuccess(json.account, json.token);
|
|
2692
2910
|
} catch (err) {
|
|
2693
|
-
setCodeError(err
|
|
2911
|
+
setCodeError(failureMessage(err, "email", "submit"));
|
|
2694
2912
|
} finally {
|
|
2695
2913
|
setSubmitting(false);
|
|
2696
2914
|
}
|
|
2697
2915
|
}
|
|
2698
2916
|
if (stage === "enter") {
|
|
2699
2917
|
return /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: start, "data-oc-signin-email": "", children: [
|
|
2700
|
-
/* @__PURE__ */ jsxRuntime.jsx(FlowHeader, {
|
|
2701
|
-
/* @__PURE__ */ jsxRuntime.jsx(Label, { children: "email" }),
|
|
2918
|
+
/* @__PURE__ */ jsxRuntime.jsx(FlowHeader, { children: "We email you a 6-digit code. No password, and no wallet needed to start. Link a Bitcoin address any time \u2014 that is the identity you hold yourself." }),
|
|
2919
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: `${uid}-email`, children: "email" }),
|
|
2702
2920
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2703
2921
|
"input",
|
|
2704
2922
|
{
|
|
2923
|
+
id: `${uid}-email`,
|
|
2705
2924
|
type: "email",
|
|
2706
2925
|
autoComplete: "email",
|
|
2707
2926
|
value: email,
|
|
@@ -2716,20 +2935,20 @@ function EmailFlow({ authOrigin, add, onSuccess }) {
|
|
|
2716
2935
|
}
|
|
2717
2936
|
),
|
|
2718
2937
|
emailError && /* @__PURE__ */ jsxRuntime.jsx(ErrorLine2, { children: emailError }),
|
|
2719
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", disabled: submitting, style: submitStyle(submitting), children: submitting ? "sending\u2026" : "send one-time code \u2192" })
|
|
2720
|
-
/* @__PURE__ */ jsxRuntime.jsx(Hint, { children: "Codes come from the auth host, expire in 10 minutes, and work once." })
|
|
2938
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", disabled: submitting, style: submitStyle(submitting), children: submitting ? "sending\u2026" : "send one-time code \u2192" })
|
|
2721
2939
|
] });
|
|
2722
2940
|
}
|
|
2723
2941
|
return /* @__PURE__ */ jsxRuntime.jsxs("form", { onSubmit: verify, "data-oc-signin-email-verify": "", children: [
|
|
2724
|
-
/* @__PURE__ */ jsxRuntime.jsxs(FlowHeader, {
|
|
2725
|
-
"Sent a 6-digit code to ",
|
|
2942
|
+
/* @__PURE__ */ jsxRuntime.jsxs(FlowHeader, { children: [
|
|
2943
|
+
resent ? "Sent a new code to " : "Sent a 6-digit code to ",
|
|
2726
2944
|
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "var(--foreground, #fafafa)" }, children: email }),
|
|
2727
|
-
".
|
|
2945
|
+
". It expires in 10 minutes."
|
|
2728
2946
|
] }),
|
|
2729
|
-
/* @__PURE__ */ jsxRuntime.jsx(Label, { children: "one-time code" }),
|
|
2947
|
+
/* @__PURE__ */ jsxRuntime.jsx(Label, { htmlFor: `${uid}-code`, children: "one-time code" }),
|
|
2730
2948
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2731
2949
|
"input",
|
|
2732
2950
|
{
|
|
2951
|
+
id: `${uid}-code`,
|
|
2733
2952
|
type: "text",
|
|
2734
2953
|
inputMode: "numeric",
|
|
2735
2954
|
pattern: "\\d{6}",
|
|
@@ -2757,31 +2976,32 @@ function EmailFlow({ authOrigin, add, onSuccess }) {
|
|
|
2757
2976
|
children: submitting ? "verifying\u2026" : "verify \xB7 sign me in \u2192"
|
|
2758
2977
|
}
|
|
2759
2978
|
),
|
|
2760
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2979
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2980
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2981
|
+
"button",
|
|
2982
|
+
{
|
|
2983
|
+
type: "button",
|
|
2984
|
+
onClick: () => void resend(),
|
|
2985
|
+
disabled: submitting,
|
|
2986
|
+
style: linkButtonStyle,
|
|
2987
|
+
children: "send a new code"
|
|
2988
|
+
}
|
|
2989
|
+
),
|
|
2990
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2991
|
+
"button",
|
|
2992
|
+
{
|
|
2993
|
+
type: "button",
|
|
2994
|
+
onClick: () => {
|
|
2995
|
+
setStage("enter");
|
|
2996
|
+
setCode("");
|
|
2997
|
+
setToken(null);
|
|
2998
|
+
setResent(false);
|
|
2999
|
+
},
|
|
3000
|
+
style: linkButtonStyle,
|
|
3001
|
+
children: "use a different email"
|
|
3002
|
+
}
|
|
3003
|
+
)
|
|
3004
|
+
] })
|
|
2785
3005
|
] });
|
|
2786
3006
|
}
|
|
2787
3007
|
function FlowHeader({
|
|
@@ -2789,15 +3009,15 @@ function FlowHeader({
|
|
|
2789
3009
|
children
|
|
2790
3010
|
}) {
|
|
2791
3011
|
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: 16 }, children: [
|
|
2792
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3012
|
+
label && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2793
3013
|
"div",
|
|
2794
3014
|
{
|
|
2795
3015
|
style: {
|
|
2796
3016
|
color: "var(--primary, #f97316)",
|
|
2797
|
-
fontFamily:
|
|
2798
|
-
fontSize: 10,
|
|
2799
|
-
letterSpacing: "0.18em",
|
|
2800
|
-
textTransform:
|
|
3017
|
+
fontFamily: SIGNIN_FONT,
|
|
3018
|
+
fontSize: signinSize(10),
|
|
3019
|
+
letterSpacing: signinTracking("0.18em"),
|
|
3020
|
+
textTransform: SIGNIN_CASE,
|
|
2801
3021
|
marginBottom: 6
|
|
2802
3022
|
},
|
|
2803
3023
|
children: label
|
|
@@ -2817,17 +3037,37 @@ function FlowHeader({
|
|
|
2817
3037
|
)
|
|
2818
3038
|
] });
|
|
2819
3039
|
}
|
|
2820
|
-
|
|
3040
|
+
var linkButtonStyle = {
|
|
3041
|
+
display: "inline-flex",
|
|
3042
|
+
alignItems: "center",
|
|
3043
|
+
minHeight: 44,
|
|
3044
|
+
marginTop: 4,
|
|
3045
|
+
marginRight: 16,
|
|
3046
|
+
background: "transparent",
|
|
3047
|
+
border: "none",
|
|
3048
|
+
color: "var(--muted-foreground, #a1a1aa)",
|
|
3049
|
+
fontFamily: SIGNIN_FONT,
|
|
3050
|
+
fontSize: signinSize(11),
|
|
3051
|
+
letterSpacing: signinTracking("0.12em"),
|
|
3052
|
+
textTransform: SIGNIN_CASE,
|
|
3053
|
+
cursor: "pointer",
|
|
3054
|
+
padding: 0
|
|
3055
|
+
};
|
|
3056
|
+
function Label({
|
|
3057
|
+
htmlFor,
|
|
3058
|
+
children
|
|
3059
|
+
}) {
|
|
2821
3060
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
2822
3061
|
"label",
|
|
2823
3062
|
{
|
|
3063
|
+
htmlFor,
|
|
2824
3064
|
style: {
|
|
2825
3065
|
display: "block",
|
|
2826
3066
|
color: "var(--foreground, #fafafa)",
|
|
2827
|
-
fontFamily:
|
|
2828
|
-
fontSize: 11,
|
|
2829
|
-
letterSpacing: "0.12em",
|
|
2830
|
-
textTransform:
|
|
3067
|
+
fontFamily: SIGNIN_FONT,
|
|
3068
|
+
fontSize: signinSize(11),
|
|
3069
|
+
letterSpacing: signinTracking("0.12em"),
|
|
3070
|
+
textTransform: SIGNIN_CASE,
|
|
2831
3071
|
marginBottom: 6
|
|
2832
3072
|
},
|
|
2833
3073
|
children
|
|
@@ -2840,8 +3080,8 @@ var inputStyle2 = {
|
|
|
2840
3080
|
background: "var(--background, #0a0a0a)",
|
|
2841
3081
|
color: "var(--foreground, #fafafa)",
|
|
2842
3082
|
border: "1px solid var(--input, #27272a)",
|
|
2843
|
-
borderRadius: 6,
|
|
2844
|
-
fontFamily:
|
|
3083
|
+
borderRadius: signinRadius(6),
|
|
3084
|
+
fontFamily: SIGNIN_FONT,
|
|
2845
3085
|
// 16px — never below it. A sub-16px font-size makes iOS Safari
|
|
2846
3086
|
// auto-zoom the viewport on focus; the family rule is >=16px on
|
|
2847
3087
|
// every form field. Desktop reads fine at 16 too.
|
|
@@ -2850,14 +3090,16 @@ var inputStyle2 = {
|
|
|
2850
3090
|
};
|
|
2851
3091
|
var linkAlsoStyle = {
|
|
2852
3092
|
display: "flex",
|
|
2853
|
-
alignItems: "
|
|
3093
|
+
alignItems: "center",
|
|
3094
|
+
// The whole row is the checkbox's hit area; the 13px box alone was not.
|
|
3095
|
+
minHeight: 44,
|
|
2854
3096
|
gap: 8,
|
|
2855
3097
|
marginTop: 16,
|
|
2856
3098
|
paddingTop: 14,
|
|
2857
3099
|
borderTop: "1px solid var(--border, #27272a)",
|
|
2858
3100
|
color: "var(--muted-foreground, #a1a1aa)",
|
|
2859
|
-
fontFamily:
|
|
2860
|
-
fontSize: 12,
|
|
3101
|
+
fontFamily: SIGNIN_FONT,
|
|
3102
|
+
fontSize: signinSize(12),
|
|
2861
3103
|
lineHeight: 1.5,
|
|
2862
3104
|
cursor: "pointer"
|
|
2863
3105
|
};
|
|
@@ -2868,12 +3110,12 @@ function submitStyle(disabled) {
|
|
|
2868
3110
|
background: "var(--primary, #f97316)",
|
|
2869
3111
|
color: "var(--primary-foreground, #0a0a0a)",
|
|
2870
3112
|
border: "none",
|
|
2871
|
-
borderRadius:
|
|
2872
|
-
fontFamily:
|
|
2873
|
-
fontSize: 11,
|
|
2874
|
-
letterSpacing: "0.14em",
|
|
2875
|
-
textTransform:
|
|
2876
|
-
fontWeight:
|
|
3113
|
+
borderRadius: SIGNIN_BUTTON_RADIUS,
|
|
3114
|
+
fontFamily: SIGNIN_FONT,
|
|
3115
|
+
fontSize: signinSize(11),
|
|
3116
|
+
letterSpacing: signinTracking("0.14em"),
|
|
3117
|
+
textTransform: SIGNIN_CASE,
|
|
3118
|
+
fontWeight: SIGNIN_STRONG,
|
|
2877
3119
|
cursor: disabled ? "not-allowed" : "pointer",
|
|
2878
3120
|
opacity: disabled ? 0.5 : 1,
|
|
2879
3121
|
width: "100%"
|
|
@@ -2888,8 +3130,8 @@ function ErrorLine2({ children }) {
|
|
|
2888
3130
|
marginTop: 6,
|
|
2889
3131
|
marginBottom: 0,
|
|
2890
3132
|
color: "var(--destructive, #ef4444)",
|
|
2891
|
-
fontFamily:
|
|
2892
|
-
fontSize: 11,
|
|
3133
|
+
fontFamily: SIGNIN_FONT,
|
|
3134
|
+
fontSize: signinSize(11),
|
|
2893
3135
|
letterSpacing: "0.04em"
|
|
2894
3136
|
},
|
|
2895
3137
|
children
|
|
@@ -2904,10 +3146,9 @@ function Hint({ children }) {
|
|
|
2904
3146
|
marginTop: 12,
|
|
2905
3147
|
marginBottom: 0,
|
|
2906
3148
|
color: "var(--muted-foreground, #a1a1aa)",
|
|
2907
|
-
fontFamily:
|
|
3149
|
+
fontFamily: SIGNIN_FONT,
|
|
2908
3150
|
fontSize: 10.5,
|
|
2909
|
-
lineHeight: 1.55
|
|
2910
|
-
opacity: 0.7
|
|
3151
|
+
lineHeight: 1.55
|
|
2911
3152
|
},
|
|
2912
3153
|
children: [
|
|
2913
3154
|
"> ",
|
|
@@ -2926,13 +3167,13 @@ function isAbortError(err) {
|
|
|
2926
3167
|
}
|
|
2927
3168
|
function useWebAuthnRegister(opts) {
|
|
2928
3169
|
const authOrigin = resolveAuthOrigin(opts);
|
|
2929
|
-
const [status, setStatus] =
|
|
2930
|
-
const [error, setError] =
|
|
2931
|
-
const reset =
|
|
3170
|
+
const [status, setStatus] = React4__namespace.useState("idle");
|
|
3171
|
+
const [error, setError] = React4__namespace.useState(null);
|
|
3172
|
+
const reset = React4__namespace.useCallback(() => {
|
|
2932
3173
|
setStatus("idle");
|
|
2933
3174
|
setError(null);
|
|
2934
3175
|
}, []);
|
|
2935
|
-
const register =
|
|
3176
|
+
const register = React4__namespace.useCallback(
|
|
2936
3177
|
async (args) => {
|
|
2937
3178
|
setError(null);
|
|
2938
3179
|
setStatus("requesting-options");
|
|
@@ -2994,10 +3235,10 @@ function useWebAuthnRegister(opts) {
|
|
|
2994
3235
|
function useWebAuthnList(opts) {
|
|
2995
3236
|
const authOrigin = resolveAuthOrigin(opts);
|
|
2996
3237
|
const { status: sessionStatus } = useOcSession();
|
|
2997
|
-
const [credentials, setCredentials] =
|
|
2998
|
-
const [status, setStatus] =
|
|
2999
|
-
const [error, setError] =
|
|
3000
|
-
const refetch =
|
|
3238
|
+
const [credentials, setCredentials] = React4__namespace.useState([]);
|
|
3239
|
+
const [status, setStatus] = React4__namespace.useState("loading");
|
|
3240
|
+
const [error, setError] = React4__namespace.useState(null);
|
|
3241
|
+
const refetch = React4__namespace.useCallback(async () => {
|
|
3001
3242
|
setError(null);
|
|
3002
3243
|
if (sessionStatus !== "authenticated") {
|
|
3003
3244
|
setCredentials([]);
|
|
@@ -3025,10 +3266,10 @@ function useWebAuthnList(opts) {
|
|
|
3025
3266
|
setStatus("error");
|
|
3026
3267
|
}
|
|
3027
3268
|
}, [authOrigin, sessionStatus]);
|
|
3028
|
-
|
|
3269
|
+
React4__namespace.useEffect(() => {
|
|
3029
3270
|
void refetch();
|
|
3030
3271
|
}, [refetch]);
|
|
3031
|
-
const rename =
|
|
3272
|
+
const rename = React4__namespace.useCallback(
|
|
3032
3273
|
async (id, label) => {
|
|
3033
3274
|
try {
|
|
3034
3275
|
const r = await fetch(`${authOrigin}/api/auth/webauthn/credentials/${id}`, {
|
|
@@ -3053,7 +3294,7 @@ function useWebAuthnList(opts) {
|
|
|
3053
3294
|
},
|
|
3054
3295
|
[authOrigin, refetch]
|
|
3055
3296
|
);
|
|
3056
|
-
const remove =
|
|
3297
|
+
const remove = React4__namespace.useCallback(
|
|
3057
3298
|
async (id) => {
|
|
3058
3299
|
try {
|
|
3059
3300
|
const r = await fetch(`${authOrigin}/api/auth/webauthn/credentials/${id}`, {
|
|
@@ -3081,13 +3322,13 @@ function useWebAuthnList(opts) {
|
|
|
3081
3322
|
function useStepUpAuth(opts) {
|
|
3082
3323
|
const authOrigin = resolveAuthOrigin(opts);
|
|
3083
3324
|
const { refresh } = useOcSession();
|
|
3084
|
-
const [status, setStatus] =
|
|
3085
|
-
const [error, setError] =
|
|
3086
|
-
const reset =
|
|
3325
|
+
const [status, setStatus] = React4__namespace.useState("idle");
|
|
3326
|
+
const [error, setError] = React4__namespace.useState(null);
|
|
3327
|
+
const reset = React4__namespace.useCallback(() => {
|
|
3087
3328
|
setStatus("idle");
|
|
3088
3329
|
setError(null);
|
|
3089
3330
|
}, []);
|
|
3090
|
-
const stepUp =
|
|
3331
|
+
const stepUp = React4__namespace.useCallback(
|
|
3091
3332
|
async (args) => {
|
|
3092
3333
|
setError(null);
|
|
3093
3334
|
setStatus("requesting-options");
|