@orangecheck/auth-client 2.29.0 → 2.30.1
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 +25 -1
- package/dist/index.d.ts +25 -1
- package/dist/index.js +294 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +292 -1
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/index.mjs
CHANGED
|
@@ -3185,6 +3185,297 @@ function Hint({ children }) {
|
|
|
3185
3185
|
}
|
|
3186
3186
|
);
|
|
3187
3187
|
}
|
|
3188
|
+
var DEFAULT_AUTH_ORIGIN2 = "https://ochk.io";
|
|
3189
|
+
var MIN_CONFIRMATIONS = 6;
|
|
3190
|
+
var n = (v) => v.toLocaleString("en-US");
|
|
3191
|
+
function bondFloorsText(f) {
|
|
3192
|
+
const sats = f.sats_floor > 0 ? `at least ${n(f.sats_floor)} sats` : "under 1,000 sats";
|
|
3193
|
+
const days = f.days_floor > 0 ? `held ${f.days_floor} days or more` : "held under 7 days";
|
|
3194
|
+
return `${sats}, ${days}`;
|
|
3195
|
+
}
|
|
3196
|
+
var short = (outpoint) => {
|
|
3197
|
+
const [txid = "", vout = ""] = outpoint.split(":");
|
|
3198
|
+
return `${txid.slice(0, 8)}\u2026${txid.slice(-4)}:${vout}`;
|
|
3199
|
+
};
|
|
3200
|
+
var REFUSAL = {
|
|
3201
|
+
invalid_address: "that isn't a Bitcoin address we can read",
|
|
3202
|
+
chain_unavailable: "couldn't read the chain just now \xB7 nothing changed, try again in a minute",
|
|
3203
|
+
sig_invalid: "that signature isn't from this coin's address \xB7 sign with the wallet that holds it",
|
|
3204
|
+
bond_expired: "the message expired \xB7 start again",
|
|
3205
|
+
bond_not_yet_valid: "your device's clock looks wrong",
|
|
3206
|
+
bond_subject_mismatch: "that message was for another account \xB7 start again",
|
|
3207
|
+
bond_malformed: "the signed message changed on the way \xB7 start again",
|
|
3208
|
+
coin_not_found: "that coin doesn't exist on-chain",
|
|
3209
|
+
coin_address_mismatch: "that coin isn't at this address",
|
|
3210
|
+
coin_unconfirmed: "that coin isn't confirmed yet",
|
|
3211
|
+
coin_too_recent: "that coin needs six confirmations first, about an hour",
|
|
3212
|
+
coin_spent: "that coin has been spent \xB7 pick one still in your wallet",
|
|
3213
|
+
coin_backs_another_account: "that coin already backs another orangecheck account",
|
|
3214
|
+
not_authenticated: "your session ended \xB7 sign in again"
|
|
3215
|
+
};
|
|
3216
|
+
var refusal = (reason) => reason && REFUSAL[reason] || "your backing didn't save \xB7 nothing changed, try again";
|
|
3217
|
+
async function fetchOcBond(opts) {
|
|
3218
|
+
const r = await fetch(`${opts?.authOrigin ?? DEFAULT_AUTH_ORIGIN2}/api/auth/bond`, {
|
|
3219
|
+
credentials: "include"
|
|
3220
|
+
});
|
|
3221
|
+
const j = await r.json().catch(() => ({}));
|
|
3222
|
+
if (!r.ok || !j.ok) throw new Error(`http_${r.status}`);
|
|
3223
|
+
return j.bond ?? null;
|
|
3224
|
+
}
|
|
3225
|
+
function OcBond({
|
|
3226
|
+
authOrigin = DEFAULT_AUTH_ORIGIN2,
|
|
3227
|
+
onChange,
|
|
3228
|
+
className
|
|
3229
|
+
}) {
|
|
3230
|
+
const [bond, setBond] = React4.useState(void 0);
|
|
3231
|
+
const [loadErr, setLoadErr] = React4.useState(false);
|
|
3232
|
+
const [nonce, setNonce] = React4.useState(0);
|
|
3233
|
+
const [adding, setAdding] = React4.useState(false);
|
|
3234
|
+
const [busy, setBusy] = React4.useState(false);
|
|
3235
|
+
const [err, setErr] = React4.useState(null);
|
|
3236
|
+
React4.useEffect(() => {
|
|
3237
|
+
let alive = true;
|
|
3238
|
+
fetchOcBond({ authOrigin }).then(
|
|
3239
|
+
(b) => alive && setBond(b),
|
|
3240
|
+
() => alive && setLoadErr(true)
|
|
3241
|
+
);
|
|
3242
|
+
return () => {
|
|
3243
|
+
alive = false;
|
|
3244
|
+
};
|
|
3245
|
+
}, [authOrigin, nonce]);
|
|
3246
|
+
const call = React4.useCallback(
|
|
3247
|
+
async (method, body) => {
|
|
3248
|
+
const r = await fetch(`${authOrigin}/api/auth/bond`, {
|
|
3249
|
+
method,
|
|
3250
|
+
credentials: "include",
|
|
3251
|
+
headers: body ? { "Content-Type": "application/json" } : void 0,
|
|
3252
|
+
body: body ? JSON.stringify(body) : void 0
|
|
3253
|
+
});
|
|
3254
|
+
const j = await r.json().catch(() => ({}));
|
|
3255
|
+
return { ok: r.ok && j.ok === true, ...j };
|
|
3256
|
+
},
|
|
3257
|
+
[authOrigin]
|
|
3258
|
+
);
|
|
3259
|
+
async function release() {
|
|
3260
|
+
setBusy(true);
|
|
3261
|
+
setErr(null);
|
|
3262
|
+
const j = await call("DELETE").catch(() => ({ ok: false, reason: "network" }));
|
|
3263
|
+
setBusy(false);
|
|
3264
|
+
if (!j.ok) return setErr(refusal(j.reason));
|
|
3265
|
+
setBond(null);
|
|
3266
|
+
onChange?.(null);
|
|
3267
|
+
}
|
|
3268
|
+
if (loadErr) {
|
|
3269
|
+
return /* @__PURE__ */ jsxs("div", { className, "data-oc-bond": "", style: panelStyle("warn"), role: "alert", children: [
|
|
3270
|
+
/* @__PURE__ */ jsx("p", { style: bodyStyle, children: "couldn't read your backing \xB7 nothing was changed" }),
|
|
3271
|
+
/* @__PURE__ */ jsx(
|
|
3272
|
+
"button",
|
|
3273
|
+
{
|
|
3274
|
+
type: "button",
|
|
3275
|
+
style: ghostBtnStyle(false),
|
|
3276
|
+
onClick: () => {
|
|
3277
|
+
setLoadErr(false);
|
|
3278
|
+
setNonce((x) => x + 1);
|
|
3279
|
+
},
|
|
3280
|
+
children: "try again"
|
|
3281
|
+
}
|
|
3282
|
+
)
|
|
3283
|
+
] });
|
|
3284
|
+
}
|
|
3285
|
+
if (bond === void 0) {
|
|
3286
|
+
return /* @__PURE__ */ jsx("div", { className, "data-oc-bond": "", style: panelStyle(), children: /* @__PURE__ */ jsxs("span", { style: hintStyle, children: [
|
|
3287
|
+
"> ",
|
|
3288
|
+
"reading your backing\u2026"
|
|
3289
|
+
] }) });
|
|
3290
|
+
}
|
|
3291
|
+
return /* @__PURE__ */ jsxs("div", { className, "data-oc-bond": "", style: { display: "grid", gap: 16 }, children: [
|
|
3292
|
+
bond && !adding ? /* @__PURE__ */ jsxs("div", { style: panelStyle("success"), children: [
|
|
3293
|
+
/* @__PURE__ */ jsx(SectionLabel, { tone: "success", children: "\xA7 backed by bitcoin" }),
|
|
3294
|
+
/* @__PURE__ */ jsxs("p", { style: { ...bodyStyle, color: V.foreground }, children: [
|
|
3295
|
+
"apps see a coin ",
|
|
3296
|
+
bondFloorsText(bond),
|
|
3297
|
+
"."
|
|
3298
|
+
] }),
|
|
3299
|
+
/* @__PURE__ */ jsxs("p", { style: bodyStyle, children: [
|
|
3300
|
+
"coin ",
|
|
3301
|
+
short(bond.outpoint),
|
|
3302
|
+
", ",
|
|
3303
|
+
n(bond.sats),
|
|
3304
|
+
" sats \xB7 last found unspent at block ",
|
|
3305
|
+
n(bond.checked_height),
|
|
3306
|
+
" \xB7 only you and orangecheck see this line"
|
|
3307
|
+
] }),
|
|
3308
|
+
/* @__PURE__ */ jsxs(FormButtons, { children: [
|
|
3309
|
+
/* @__PURE__ */ jsx("button", { type: "button", style: ghostBtnStyle(busy), disabled: busy, onClick: () => setAdding(true), children: "use another coin" }),
|
|
3310
|
+
/* @__PURE__ */ jsx("button", { type: "button", style: dangerBtnStyle(busy), disabled: busy, onClick: () => void release(), children: "stop backing" })
|
|
3311
|
+
] }),
|
|
3312
|
+
err ? /* @__PURE__ */ jsx(ErrorLine, { children: err }) : null
|
|
3313
|
+
] }) : null,
|
|
3314
|
+
!bond && !adding ? /* @__PURE__ */ jsxs("div", { style: panelStyle(), children: [
|
|
3315
|
+
/* @__PURE__ */ jsx(SectionLabel, { children: "\xA7 not backed" }),
|
|
3316
|
+
/* @__PURE__ */ jsx("p", { style: bodyStyle, children: "your account works everywhere as it is. apps that want to keep out bots can ask for a backed account: one coin you hold, that never moves and that they never see." }),
|
|
3317
|
+
/* @__PURE__ */ jsx("button", { type: "button", style: primaryBtnStyle(false), onClick: () => setAdding(true), children: "back it with a coin" })
|
|
3318
|
+
] }) : null,
|
|
3319
|
+
adding ? /* @__PURE__ */ jsx(
|
|
3320
|
+
BondForm,
|
|
3321
|
+
{
|
|
3322
|
+
call,
|
|
3323
|
+
onCancel: () => setAdding(false),
|
|
3324
|
+
onDone: (b) => {
|
|
3325
|
+
setBond(b);
|
|
3326
|
+
setAdding(false);
|
|
3327
|
+
onChange?.(b);
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3330
|
+
) : null,
|
|
3331
|
+
/* @__PURE__ */ jsxs("p", { style: hintStyle, children: [
|
|
3332
|
+
"> ",
|
|
3333
|
+
"apps see that a coin backs you, in rough steps, and a number unique to each app, so they can't compare notes. an app can block that number; getting past it means moving the coin, and a moved coin starts its age again."
|
|
3334
|
+
] })
|
|
3335
|
+
] });
|
|
3336
|
+
}
|
|
3337
|
+
function BondForm({
|
|
3338
|
+
call,
|
|
3339
|
+
onCancel,
|
|
3340
|
+
onDone
|
|
3341
|
+
}) {
|
|
3342
|
+
const [address, setAddress] = React4.useState("");
|
|
3343
|
+
const [coins, setCoins] = React4.useState(null);
|
|
3344
|
+
const [chosen, setChosen] = React4.useState("");
|
|
3345
|
+
const [message, setMessage] = React4.useState(null);
|
|
3346
|
+
const [busy, setBusy] = React4.useState(false);
|
|
3347
|
+
const [err, setErr] = React4.useState(null);
|
|
3348
|
+
async function run(fn) {
|
|
3349
|
+
setBusy(true);
|
|
3350
|
+
setErr(null);
|
|
3351
|
+
try {
|
|
3352
|
+
await fn();
|
|
3353
|
+
} catch {
|
|
3354
|
+
setErr(refusal("chain_unavailable"));
|
|
3355
|
+
} finally {
|
|
3356
|
+
setBusy(false);
|
|
3357
|
+
}
|
|
3358
|
+
}
|
|
3359
|
+
const findCoins = () => run(async () => {
|
|
3360
|
+
const j = await call("POST", { action: "coins", address: address.trim() });
|
|
3361
|
+
if (!j.ok) return setErr(refusal(j.reason));
|
|
3362
|
+
const list = j.coins ?? [];
|
|
3363
|
+
setCoins(list);
|
|
3364
|
+
setChosen(list.find((c) => c.age_blocks >= MIN_CONFIRMATIONS)?.outpoint ?? "");
|
|
3365
|
+
});
|
|
3366
|
+
const getMessage = () => run(async () => {
|
|
3367
|
+
const j = await call("POST", { action: "message", address: address.trim(), outpoint: chosen });
|
|
3368
|
+
if (!j.ok || typeof j.message !== "string") return setErr(refusal(j.reason));
|
|
3369
|
+
setMessage(j.message);
|
|
3370
|
+
});
|
|
3371
|
+
const submit = (signature) => run(async () => {
|
|
3372
|
+
const j = await call("POST", { action: "bond", message, signature });
|
|
3373
|
+
if (!j.ok || !j.bond) return setErr(refusal(j.reason));
|
|
3374
|
+
onDone(j.bond);
|
|
3375
|
+
});
|
|
3376
|
+
return /* @__PURE__ */ jsxs("div", { style: panelStyle("accent"), "data-oc-bond-form": "", children: [
|
|
3377
|
+
/* @__PURE__ */ jsx(SectionLabel, { children: "\xA7 back your account with a coin" }),
|
|
3378
|
+
message ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3379
|
+
/* @__PURE__ */ jsxs("p", { style: bodyStyle, children: [
|
|
3380
|
+
"sign with the wallet that holds",
|
|
3381
|
+
" ",
|
|
3382
|
+
/* @__PURE__ */ jsx("span", { style: { wordBreak: "break-all", color: V.foreground }, children: address.trim() }),
|
|
3383
|
+
". the coin doesn't move and nothing is spent."
|
|
3384
|
+
] }),
|
|
3385
|
+
/* @__PURE__ */ jsx(
|
|
3386
|
+
LazyWalletButton,
|
|
3387
|
+
{
|
|
3388
|
+
address: address.trim(),
|
|
3389
|
+
message,
|
|
3390
|
+
showManual: true,
|
|
3391
|
+
onSigned: (sig) => void submit(sig),
|
|
3392
|
+
onError: (e) => setErr(e.message ?? "the wallet refused to sign")
|
|
3393
|
+
}
|
|
3394
|
+
)
|
|
3395
|
+
] }) : coins ? coins.length === 0 ? /* @__PURE__ */ jsx("p", { style: bodyStyle, children: "no confirmed coins at that address \xB7 send some sats there and wait for six confirmations, about an hour. the longer a coin sits, the more it says." }) : /* @__PURE__ */ jsxs("div", { role: "radiogroup", "aria-label": "pick one coin", style: { display: "grid", gap: 8 }, children: [
|
|
3396
|
+
/* @__PURE__ */ jsx("p", { style: bodyStyle, children: "pick one coin \xB7 value spread over several coins doesn't add up" }),
|
|
3397
|
+
coins.map((c) => {
|
|
3398
|
+
const young = c.age_blocks < MIN_CONFIRMATIONS;
|
|
3399
|
+
return /* @__PURE__ */ jsxs(
|
|
3400
|
+
"label",
|
|
3401
|
+
{
|
|
3402
|
+
style: {
|
|
3403
|
+
...panelStyle(chosen === c.outpoint ? "accent" : "default"),
|
|
3404
|
+
padding: "0.75rem",
|
|
3405
|
+
display: "flex",
|
|
3406
|
+
gap: 10,
|
|
3407
|
+
alignItems: "flex-start",
|
|
3408
|
+
cursor: young ? "not-allowed" : "pointer",
|
|
3409
|
+
opacity: young ? 0.6 : 1
|
|
3410
|
+
},
|
|
3411
|
+
children: [
|
|
3412
|
+
/* @__PURE__ */ jsx(
|
|
3413
|
+
"input",
|
|
3414
|
+
{
|
|
3415
|
+
type: "radio",
|
|
3416
|
+
name: "oc-bond-coin",
|
|
3417
|
+
value: c.outpoint,
|
|
3418
|
+
checked: chosen === c.outpoint,
|
|
3419
|
+
disabled: young,
|
|
3420
|
+
onChange: () => setChosen(c.outpoint)
|
|
3421
|
+
}
|
|
3422
|
+
),
|
|
3423
|
+
/* @__PURE__ */ jsxs("span", { style: { display: "grid", gap: 2, minWidth: 0 }, children: [
|
|
3424
|
+
/* @__PURE__ */ jsxs("span", { style: { color: V.foreground, fontSize: 13 }, children: [
|
|
3425
|
+
n(c.sats),
|
|
3426
|
+
" sats, held ",
|
|
3427
|
+
Math.floor(c.age_blocks / 144),
|
|
3428
|
+
" days"
|
|
3429
|
+
] }),
|
|
3430
|
+
/* @__PURE__ */ jsx("span", { style: hintStyle, children: young ? "too recent: needs six confirmations" : `apps would see ${bondFloorsText(c)}` }),
|
|
3431
|
+
/* @__PURE__ */ jsx("span", { style: { ...hintStyle, wordBreak: "break-all" }, children: short(c.outpoint) })
|
|
3432
|
+
] })
|
|
3433
|
+
]
|
|
3434
|
+
},
|
|
3435
|
+
c.outpoint
|
|
3436
|
+
);
|
|
3437
|
+
})
|
|
3438
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3439
|
+
/* @__PURE__ */ jsx("p", { style: bodyStyle, children: "which address holds the coin? use one that holds nothing else of yours: orangecheck learns its address and amount. apps never do." }),
|
|
3440
|
+
/* @__PURE__ */ jsx(
|
|
3441
|
+
"input",
|
|
3442
|
+
{
|
|
3443
|
+
style: inputStyle,
|
|
3444
|
+
value: address,
|
|
3445
|
+
onChange: (e) => setAddress(e.target.value),
|
|
3446
|
+
placeholder: "bc1q\u2026",
|
|
3447
|
+
spellCheck: false,
|
|
3448
|
+
autoComplete: "off",
|
|
3449
|
+
"aria-label": "Bitcoin address"
|
|
3450
|
+
}
|
|
3451
|
+
)
|
|
3452
|
+
] }),
|
|
3453
|
+
err ? /* @__PURE__ */ jsx(ErrorLine, { children: err }) : null,
|
|
3454
|
+
/* @__PURE__ */ jsxs(FormButtons, { children: [
|
|
3455
|
+
!coins && !message ? /* @__PURE__ */ jsx(
|
|
3456
|
+
"button",
|
|
3457
|
+
{
|
|
3458
|
+
type: "button",
|
|
3459
|
+
style: primaryBtnStyle(busy || address.trim().length < 14),
|
|
3460
|
+
disabled: busy || address.trim().length < 14,
|
|
3461
|
+
onClick: () => void findCoins(),
|
|
3462
|
+
children: "find its coins"
|
|
3463
|
+
}
|
|
3464
|
+
) : null,
|
|
3465
|
+
coins && coins.length > 0 && !message ? /* @__PURE__ */ jsx(
|
|
3466
|
+
"button",
|
|
3467
|
+
{
|
|
3468
|
+
type: "button",
|
|
3469
|
+
style: primaryBtnStyle(busy || !chosen),
|
|
3470
|
+
disabled: busy || !chosen,
|
|
3471
|
+
onClick: () => void getMessage(),
|
|
3472
|
+
children: "continue"
|
|
3473
|
+
}
|
|
3474
|
+
) : null,
|
|
3475
|
+
/* @__PURE__ */ jsx("button", { type: "button", style: ghostBtnStyle(busy), disabled: busy, onClick: onCancel, children: "cancel" })
|
|
3476
|
+
] })
|
|
3477
|
+
] });
|
|
3478
|
+
}
|
|
3188
3479
|
function resolveAuthOrigin(opts) {
|
|
3189
3480
|
if (!opts) return resolveConfig(void 0).authOrigin;
|
|
3190
3481
|
return resolveConfig(opts).authOrigin;
|
|
@@ -3419,6 +3710,6 @@ function useStepUpAuth(opts) {
|
|
|
3419
3710
|
return { status, error, stepUp, reset };
|
|
3420
3711
|
}
|
|
3421
3712
|
|
|
3422
|
-
export { DEFAULT_CONFIG, DISPLAY_IDENTITY_KINDS, OcAccountChip, OcAccountPill, OcAddressInput, OcLinkedIdentities, OcSessionProvider, OcSignIn, OcSignInButton, SUDO_ACCOUNT_MISMATCH_MESSAGE, TAB_ACCOUNT_HINT_KEY, TAB_ADOPT_HASH, TAB_SESSION_HEADER, TAB_SESSION_STORAGE_KEY, buildAddAccountUrl, buildSignInUrl, clearTabSession, consumeTabAccountHint, consumeTabAdoptMarker, familyReturnTarget, fetchOcLinkedIdentities, handleSudoRequired, installTabFetchInterceptor, installTabLinkDecorator, isSudoAccountMismatch, readTabSession, redirectToSudo, safeReturnTo, tabSessionHeader, useOcAddressSuggestion, useOcSession, useOptionalOcSession, useStepUpAuth, useWebAuthnList, useWebAuthnRegister, writeTabSession };
|
|
3713
|
+
export { DEFAULT_CONFIG, DISPLAY_IDENTITY_KINDS, OcAccountChip, OcAccountPill, OcAddressInput, OcBond, OcLinkedIdentities, OcSessionProvider, OcSignIn, OcSignInButton, SUDO_ACCOUNT_MISMATCH_MESSAGE, TAB_ACCOUNT_HINT_KEY, TAB_ADOPT_HASH, TAB_SESSION_HEADER, TAB_SESSION_STORAGE_KEY, bondFloorsText, buildAddAccountUrl, buildSignInUrl, clearTabSession, consumeTabAccountHint, consumeTabAdoptMarker, familyReturnTarget, fetchOcBond, fetchOcLinkedIdentities, handleSudoRequired, installTabFetchInterceptor, installTabLinkDecorator, isSudoAccountMismatch, readTabSession, redirectToSudo, safeReturnTo, tabSessionHeader, useOcAddressSuggestion, useOcSession, useOptionalOcSession, useStepUpAuth, useWebAuthnList, useWebAuthnRegister, writeTabSession };
|
|
3423
3714
|
//# sourceMappingURL=index.mjs.map
|
|
3424
3715
|
//# sourceMappingURL=index.mjs.map
|