@orangecheck/auth-client 2.7.0 → 2.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -868,7 +868,7 @@ function OcLinkedIdentities({
868
868
  onCancel: () => setShowBtcLink(false)
869
869
  }
870
870
  ),
871
- hasBtc && resp.did_oc && /* @__PURE__ */ jsx(OcIdentityBond, { didOc: resp.did_oc, btc: btcRows.find((i) => !!i.value).value })
871
+ hasBtc && /* @__PURE__ */ jsx(AttestationLink, {})
872
872
  ] });
873
873
  }
874
874
  function IdentityGroup({
@@ -1047,6 +1047,65 @@ function ConsolidationPanel({ entries }) {
1047
1047
  )) })
1048
1048
  ] });
1049
1049
  }
1050
+ function AttestationLink() {
1051
+ return /* @__PURE__ */ jsxs("div", { style: panelStyle(), "data-oc-li-attest": "", children: [
1052
+ /* @__PURE__ */ jsx(SectionLabel, { children: "\xA7 portable attestation" }),
1053
+ /* @__PURE__ */ jsx("p", { style: bodyStyle, children: "Make your identity links portable: publish a standard OrangeCheck attestation \u2014 one Bitcoin-wallet signature (BIP-322) binding your address to your handles (Nostr, GitHub, \u2026). It is published to Nostr and verifies with zero trust in OrangeCheck, so it outlives any single service." }),
1054
+ /* @__PURE__ */ jsx(
1055
+ "a",
1056
+ {
1057
+ href: "https://attest.ochk.io/create",
1058
+ target: "_blank",
1059
+ rel: "noreferrer",
1060
+ style: { ...primaryChipStyle, textDecoration: "none", display: "inline-block" },
1061
+ "data-oc-li-attest-link": "",
1062
+ children: "open the attestation builder \u2197"
1063
+ }
1064
+ )
1065
+ ] });
1066
+ }
1067
+ function LinkPromptStep({
1068
+ method,
1069
+ didOc,
1070
+ authOrigin,
1071
+ onResolved
1072
+ }) {
1073
+ const [stage, setStage] = React3.useState("offer");
1074
+ if (stage === "linking") {
1075
+ return method === "btc" ? /* @__PURE__ */ jsx(
1076
+ BtcLinkForm,
1077
+ {
1078
+ authOrigin,
1079
+ didOc,
1080
+ onDone: onResolved,
1081
+ onCancel: () => setStage("offer")
1082
+ }
1083
+ ) : /* @__PURE__ */ jsx(
1084
+ EmailLinkForm,
1085
+ {
1086
+ authOrigin,
1087
+ onDone: onResolved,
1088
+ onCancel: () => setStage("offer")
1089
+ }
1090
+ );
1091
+ }
1092
+ return /* @__PURE__ */ jsxs("div", { style: panelStyle("accent"), "data-oc-linkprompt": method, children: [
1093
+ /* @__PURE__ */ jsx(SectionLabel, { tone: "success", children: "\xA7 signed in" }),
1094
+ /* @__PURE__ */ jsx("p", { style: bodyStyle, children: method === "btc" ? "You're in. Link a Bitcoin address now \u2014 a second way to sign in, and it unlocks your on-chain footprint. You prove it with one wallet signature, right here \u2014 no separate trip to your account page." : "You're in. Add your email now \u2014 a backup way to sign in if you ever lose your wallet. You prove it with a one-time code, right here \u2014 no separate trip to your account page." }),
1095
+ /* @__PURE__ */ jsxs(FormButtons, { children: [
1096
+ /* @__PURE__ */ jsx(
1097
+ "button",
1098
+ {
1099
+ type: "button",
1100
+ onClick: () => setStage("linking"),
1101
+ style: primaryBtnStyle(false),
1102
+ children: method === "btc" ? "link a Bitcoin address" : "link an email"
1103
+ }
1104
+ ),
1105
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: onResolved, style: ghostBtnStyle(false), children: "skip" })
1106
+ ] })
1107
+ ] });
1108
+ }
1050
1109
  function EmailLinkForm({
1051
1110
  authOrigin,
1052
1111
  onDone,
@@ -1353,185 +1412,6 @@ function TransferConfirm({
1353
1412
  ] })
1354
1413
  ] });
1355
1414
  }
1356
- function OcIdentityBond({
1357
- didOc,
1358
- btc,
1359
- className
1360
- }) {
1361
- const [stage, setStage] = React3.useState("idle");
1362
- const [message, setMessage] = React3.useState("");
1363
- const [err, setErr] = React3.useState(null);
1364
- const [result, setResult] = React3.useState(null);
1365
- function nip07() {
1366
- if (typeof window === "undefined") return null;
1367
- const w = window;
1368
- return w.nostr ?? null;
1369
- }
1370
- async function begin() {
1371
- setErr(null);
1372
- const nostr = nip07();
1373
- if (!nostr) {
1374
- setErr(
1375
- "no Nostr signer found \xB7 install a NIP-07 extension (Alby, nos2x) to counter-sign the bond"
1376
- );
1377
- return;
1378
- }
1379
- try {
1380
- const sdk = await loadSdk();
1381
- const pubkeyHex = (await nostr.getPublicKey()).toLowerCase();
1382
- const npub = sdk.xOnlyHexToNpub(pubkeyHex);
1383
- const msg = sdk.buildBindingMessage({
1384
- principal: didOc,
1385
- btc,
1386
- nostr: npub,
1387
- nonce: randomNonceHex(),
1388
- issued_at: (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "Z")
1389
- });
1390
- setMessage(msg);
1391
- setStage("sign-btc");
1392
- } catch (e) {
1393
- setErr(e instanceof Error ? e.message : "could not start the bond ceremony");
1394
- }
1395
- }
1396
- async function afterBtcSign(btcSignature) {
1397
- setErr(null);
1398
- setStage("sign-nostr");
1399
- try {
1400
- const nostr = nip07();
1401
- if (!nostr) throw new Error("Nostr extension went away \xB7 retry");
1402
- const sdk = await loadSdk();
1403
- const pubkeyHex = (await nostr.getPublicKey()).toLowerCase();
1404
- const template = sdk.buildBindingEventTemplate({
1405
- message,
1406
- btcSignature,
1407
- nostrPubkeyHex: pubkeyHex,
1408
- btc
1409
- });
1410
- const signedEvent = await nostr.signEvent(template);
1411
- const envelope = sdk.assembleBindingEnvelope({
1412
- message,
1413
- btcSignature,
1414
- btcScheme: "bip322",
1415
- nostrEvent: signedEvent
1416
- });
1417
- const check = sdk.verifyBinding(envelope);
1418
- if (!check.valid) {
1419
- throw new Error(`assembled bond failed local verification (${check.status})`);
1420
- }
1421
- setStage("publishing");
1422
- const { publishEvent } = await loadNostrCore();
1423
- const results = await publishEvent(signedEvent);
1424
- const ok = results.filter((r) => r.ok).length;
1425
- if (ok === 0) {
1426
- throw new Error("no relay accepted the bond \xB7 check your connection and retry");
1427
- }
1428
- setResult({
1429
- bindingId: check.valid ? check.binding_id : sdk.bindingId(message),
1430
- relays: ok,
1431
- relaysTotal: results.length
1432
- });
1433
- setStage("done");
1434
- } catch (e) {
1435
- setErr(e instanceof Error ? e.message : "bond publish failed");
1436
- setStage("sign-btc");
1437
- }
1438
- }
1439
- return /* @__PURE__ */ jsxs("div", { className, style: panelStyle("accent"), "data-oc-identity-bond": "", children: [
1440
- /* @__PURE__ */ jsx(SectionLabel, { children: "\xA7 identity bond \xB7 publish to nostr" }),
1441
- stage === "idle" && /* @__PURE__ */ jsxs(Fragment, { children: [
1442
- /* @__PURE__ */ jsxs("p", { style: bodyStyle, children: [
1443
- "Publish a ",
1444
- /* @__PURE__ */ jsx("strong", { children: "Binding Attestation" }),
1445
- " \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."
1446
- ] }),
1447
- /* @__PURE__ */ jsx(FormButtons, { children: /* @__PURE__ */ jsx(
1448
- "button",
1449
- {
1450
- type: "button",
1451
- onClick: () => void begin(),
1452
- style: primaryBtnStyle(false),
1453
- children: "create & publish bond"
1454
- }
1455
- ) })
1456
- ] }),
1457
- stage === "sign-btc" && /* @__PURE__ */ jsxs(Fragment, { children: [
1458
- /* @__PURE__ */ jsxs("p", { style: bodyStyle, children: [
1459
- "Step 1 of 2 \u2014 sign the binding message with the Bitcoin wallet that controls ",
1460
- /* @__PURE__ */ jsx("span", { style: { fontFamily: MONO, wordBreak: "break-all" }, children: btc }),
1461
- "."
1462
- ] }),
1463
- /* @__PURE__ */ jsx(
1464
- LazyWalletButton,
1465
- {
1466
- address: btc,
1467
- message,
1468
- showManual: true,
1469
- layout: "list",
1470
- heading: null,
1471
- onSigned: (sig) => void afterBtcSign(sig),
1472
- onError: (e) => setErr(e?.message ?? "wallet rejected the signature")
1473
- }
1474
- )
1475
- ] }),
1476
- (stage === "sign-nostr" || stage === "publishing") && /* @__PURE__ */ 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" }),
1477
- stage === "done" && result && /* @__PURE__ */ jsxs(Fragment, { children: [
1478
- /* @__PURE__ */ jsx(SectionLabel, { tone: "success", children: "\xA7 bond published" }),
1479
- /* @__PURE__ */ jsxs("p", { style: bodyStyle, children: [
1480
- "Your identity bond is live on ",
1481
- result.relays,
1482
- "/",
1483
- result.relaysTotal,
1484
- " ",
1485
- "relay",
1486
- result.relays === 1 ? "" : "s",
1487
- ". Anyone can now verify the BTC \u21C4 Nostr binding offline."
1488
- ] }),
1489
- /* @__PURE__ */ jsxs(
1490
- "div",
1491
- {
1492
- style: {
1493
- fontFamily: MONO,
1494
- fontSize: 11,
1495
- color: V.muted,
1496
- wordBreak: "break-all"
1497
- },
1498
- children: [
1499
- "binding_id: ",
1500
- result.bindingId
1501
- ]
1502
- }
1503
- )
1504
- ] }),
1505
- err && /* @__PURE__ */ jsx(ErrorLine, { children: err })
1506
- ] });
1507
- }
1508
- function randomNonceHex() {
1509
- const b = new Uint8Array(16);
1510
- if (typeof crypto !== "undefined" && crypto.getRandomValues) {
1511
- crypto.getRandomValues(b);
1512
- } else {
1513
- for (let i = 0; i < 16; i++) b[i] = Math.floor(Math.random() * 256);
1514
- }
1515
- let out = "";
1516
- for (const x of b) out += x.toString(16).padStart(2, "0");
1517
- return out;
1518
- }
1519
- async function loadSdk() {
1520
- try {
1521
- return await import('@orangecheck/sdk');
1522
- } catch {
1523
- throw new Error("@orangecheck/sdk not installed \xB7 add it to your site to publish bonds");
1524
- }
1525
- }
1526
- async function loadNostrCore() {
1527
- try {
1528
- return await import('@orangecheck/nostr-core');
1529
- } catch {
1530
- throw new Error(
1531
- "@orangecheck/nostr-core not installed \xB7 add it to your site to publish bonds"
1532
- );
1533
- }
1534
- }
1535
1415
  function LazyWalletButton(props) {
1536
1416
  const [Comp, setComp] = React3.useState(null);
1537
1417
  const [failed, setFailed] = React3.useState(false);
@@ -1734,7 +1614,7 @@ function OcSignIn({
1734
1614
  returnTo,
1735
1615
  onSuccess,
1736
1616
  resolveReturnTo,
1737
- linkPrompt,
1617
+ linkPrompt = true,
1738
1618
  authOrigin = "https://ochk.io",
1739
1619
  initialPath = "wallet",
1740
1620
  paths,
@@ -1759,18 +1639,34 @@ function OcSignIn({
1759
1639
  [resolveReturnTo, safeReturn]
1760
1640
  );
1761
1641
  const handleSuccess = React3.useCallback(
1762
- async (account, token) => {
1763
- if (onSuccess) {
1764
- onSuccess(account, token);
1642
+ async (account, token, via) => {
1643
+ const proceed = () => {
1644
+ if (onSuccess) onSuccess(account, token);
1645
+ else void navigate(account);
1646
+ };
1647
+ if (!linkPrompt) {
1648
+ proceed();
1765
1649
  return;
1766
1650
  }
1767
- if (linkPrompt) {
1768
- setSignedIn(account);
1769
- return;
1651
+ try {
1652
+ const meRes = await fetch(`${authOrigin}/api/auth/me`, {
1653
+ credentials: "include",
1654
+ headers: { Accept: "application/json" }
1655
+ });
1656
+ const me = meRes.ok ? await meRes.json() : null;
1657
+ const acct = me?.account;
1658
+ const didOc = acct?.did_oc;
1659
+ const complementary = via === "email" ? "btc" : "email";
1660
+ const alreadyLinked = complementary === "btc" ? Boolean(acct?.primary_btc) : Boolean(acct?.has_email);
1661
+ if (didOc && !alreadyLinked) {
1662
+ setSignedIn({ method: complementary, didOc, proceed });
1663
+ return;
1664
+ }
1665
+ } catch {
1770
1666
  }
1771
- await navigate(account);
1667
+ proceed();
1772
1668
  },
1773
- [onSuccess, linkPrompt, navigate]
1669
+ [onSuccess, linkPrompt, navigate, authOrigin]
1774
1670
  );
1775
1671
  if (!walletEnabled && !emailEnabled) {
1776
1672
  return /* @__PURE__ */ jsxs(
@@ -1801,8 +1697,10 @@ function OcSignIn({
1801
1697
  return /* @__PURE__ */ jsx("div", { className, "data-oc-signin": "", children: /* @__PURE__ */ jsx(
1802
1698
  LinkPromptStep,
1803
1699
  {
1700
+ method: signedIn.method,
1701
+ didOc: signedIn.didOc,
1804
1702
  authOrigin,
1805
- onContinue: () => void navigate(signedIn)
1703
+ onResolved: signedIn.proceed
1806
1704
  }
1807
1705
  ) });
1808
1706
  }
@@ -1841,32 +1739,19 @@ function OcSignIn({
1841
1739
  {
1842
1740
  authOrigin,
1843
1741
  audience,
1844
- onSuccess: handleSuccess
1742
+ onSuccess: (a, t) => void handleSuccess(a, t, "wallet")
1845
1743
  }
1846
1744
  ),
1847
- showEmail && /* @__PURE__ */ jsx(EmailFlow, { authOrigin, onSuccess: handleSuccess })
1745
+ showEmail && /* @__PURE__ */ jsx(
1746
+ EmailFlow,
1747
+ {
1748
+ authOrigin,
1749
+ onSuccess: (a, t) => void handleSuccess(a, t, "email")
1750
+ }
1751
+ )
1848
1752
  ] })
1849
1753
  ] });
1850
1754
  }
1851
- function LinkPromptStep({
1852
- authOrigin,
1853
- onContinue
1854
- }) {
1855
- return /* @__PURE__ */ jsxs("div", { "data-oc-signin-linkprompt": "", children: [
1856
- /* @__PURE__ */ jsx(FlowHeader, { label: "\xA7 signed in \xB7 add a backup", children: "You're in. Linking a second way to sign in \u2014 your email or your Bitcoin wallet \u2014 is also your recovery path: lose one, the other still gets you in. Optional; skip with continue." }),
1857
- /* @__PURE__ */ jsx(OcLinkedIdentities, { authOrigin }),
1858
- /* @__PURE__ */ jsx(
1859
- "button",
1860
- {
1861
- type: "button",
1862
- onClick: onContinue,
1863
- style: submitStyle(false),
1864
- "data-oc-signin-continue": "",
1865
- children: "continue \u2192"
1866
- }
1867
- )
1868
- ] });
1869
- }
1870
1755
  function SigninTab({
1871
1756
  active,
1872
1757
  onClick,
@@ -2526,6 +2411,6 @@ function handleSudoRequired(body, args = {}) {
2526
2411
  return false;
2527
2412
  }
2528
2413
 
2529
- export { DEFAULT_CONFIG, OcAccountChip, OcAccountPill, OcAddressInput, OcIdentityBond, OcLinkedIdentities, OcSessionProvider, OcSignIn, OcSignInButton, buildSignInUrl, handleSudoRequired, redirectToSudo, useOcAddressSuggestion, useOcSession, useOptionalOcSession, useStepUpAuth, useWebAuthnList, useWebAuthnRegister };
2414
+ export { DEFAULT_CONFIG, OcAccountChip, OcAccountPill, OcAddressInput, OcLinkedIdentities, OcSessionProvider, OcSignIn, OcSignInButton, buildSignInUrl, handleSudoRequired, redirectToSudo, useOcAddressSuggestion, useOcSession, useOptionalOcSession, useStepUpAuth, useWebAuthnList, useWebAuthnRegister };
2530
2415
  //# sourceMappingURL=index.mjs.map
2531
2416
  //# sourceMappingURL=index.mjs.map