@orangecheck/auth-client 2.34.0 → 2.35.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/linked-identities.tsx"],"sourcesContent":["'use client';\n\n/**\n * @orangecheck/auth-client · OcLinkedIdentities\n *\n * The shared linked-identities management surface for every `X.ochk.io`\n * consumer. A signed-in user sees their linked email + Bitcoin address,\n * can reveal/hide each raw value, unlink a non-primary identity, and\n * link a new one. Linking a Bitcoin address that's already on another\n * OC account offers a dual-proof transfer; the same is now true for\n * email (auth host F4). Consolidated (`merged_from`) accounts render in\n * a tombstone panel.\n *\n * This generalizes me.ochk.io's bespoke `LinkedIdentitiesPanel` so\n * linking is no longer a single-product, one-tab feature — any family\n * site can drop in `<OcLinkedIdentities />`. See AUTH-PLAN.md §5.\n *\n * All endpoints live on the auth host (`ochk.io`); calls are\n * cross-origin with `credentials: 'include'` and rely on the auth\n * host's family-CORS — the same posture `OcSignIn` uses. No local\n * proxy route is needed (AUTH-AUDIT.md F6: direct-to-host is the\n * intended design).\n *\n * Style: inline, CSS-variable-themed (same approach as `OcSignIn`), so\n * the component carries zero CSS dependency and themes to each site.\n * Every block carries a `data-oc-li-*` attribute so a site may\n * override via its own stylesheet.\n */\n\nimport * as React from 'react';\nimport {\n handleSudoRequired,\n isSudoAccountMismatch,\n SUDO_ACCOUNT_MISMATCH_MESSAGE,\n} from './sudo';\n\nimport {\n SIGNIN_BUTTON_RADIUS,\n SIGNIN_CASE,\n SIGNIN_FONT,\n signinRadius,\n signinSize,\n signinTracking,\n} from './appearance';\nimport { tabSessionHeader } from './tab-session';\n\nconst DEFAULT_AUTH_ORIGIN = 'https://ochk.io';\n\n/* --- types --- */\n\nexport interface OcLinkedIdentity {\n id: string;\n kind: 'email' | 'btc';\n /** Raw value · null when the identity is pending recovery. */\n value: string | null;\n verified_at: string | null;\n verification_method: 'email-otp' | 'bip322' | null;\n /** The signup-time identity for its kind · cannot be unlinked here. */\n is_primary: boolean;\n}\n\ninterface MergedFromEntry {\n did_oc: string;\n created_at: string;\n last_signed_in_at: string | null;\n}\n\ninterface IdentitiesResp {\n ok: boolean;\n account_id?: string;\n did_oc?: string | null;\n linked?: OcLinkedIdentity[];\n merged_from?: MergedFromEntry[];\n reason?: string;\n}\n\n/**\n * Fetch the signed-in user's linked identities from the auth host —\n * the same data `<OcLinkedIdentities>` renders, exposed as a plain\n * function for surfaces that need the list without the full\n * management UI (notably `<OcAccountMenu>`'s badge-identity promote\n * list). Returns `[]` when anonymous; throws on network / server\n * error so the caller can show a deferred state.\n */\nexport async function fetchOcLinkedIdentities(opts?: {\n authOrigin?: string;\n}): Promise<OcLinkedIdentity[]> {\n const authOrigin = opts?.authOrigin ?? DEFAULT_AUTH_ORIGIN;\n // Per-tab pinning: forward the tab's session JWT so the host resolves\n // the SAME account this tab displays (its chip reads /api/auth/me\n // tab-aware). Without this, a pinned tab's show-as list is the shared\n // cookie account's identities — a menu that disagrees with itself.\n // Unpinned tabs send no header → unchanged cookie-default behavior.\n const r = await fetch(`${authOrigin}/api/auth/identities`, {\n credentials: 'include',\n headers: { ...tabSessionHeader() },\n });\n if (r.status === 401) return [];\n if (!r.ok) throw new Error(`identities fetch failed: http_${r.status}`);\n const body = (await r.json()) as IdentitiesResp;\n return body.linked ?? [];\n}\n\nexport interface OcLinkedIdentitiesProps {\n /** Auth host origin. Defaults to `https://ochk.io`. */\n authOrigin?: string;\n /** Outer container className. */\n className?: string;\n /** Called after every successful link / unlink / transfer. */\n onChange?: () => void;\n /**\n * Show the \"portable attestation\" card that links out to attest.ochk.io.\n * Defaults to `true`. Pass `false` on a page that already hosts its own\n * attestation builder (ochk.io's dashboard), or the same action is offered\n * twice, one panel apart.\n */\n attestationLink?: boolean;\n}\n\n/* --- main --- */\n\nexport function OcLinkedIdentities({\n authOrigin = DEFAULT_AUTH_ORIGIN,\n className,\n onChange,\n attestationLink = true,\n}: OcLinkedIdentitiesProps): React.ReactElement {\n const [resp, setResp] = React.useState<IdentitiesResp | null>(null);\n const [error, setError] = React.useState<string | null>(null);\n const [showEmailLink, setShowEmailLink] = React.useState(false);\n const [showBtcLink, setShowBtcLink] = React.useState(false);\n\n const refresh = React.useCallback(async () => {\n setError(null);\n try {\n const r = await fetch(`${authOrigin}/api/auth/identities`, {\n credentials: 'include',\n headers: { ...tabSessionHeader() },\n });\n if (!r.ok) {\n if (r.status === 401) {\n setError('not signed in');\n return;\n }\n throw new Error(`http_${r.status}`);\n }\n setResp((await r.json()) as IdentitiesResp);\n } catch (e) {\n setError(e instanceof Error ? e.message : 'load failed');\n }\n }, [authOrigin]);\n\n React.useEffect(() => {\n void refresh();\n }, [refresh]);\n\n const afterChange = React.useCallback(() => {\n setShowEmailLink(false);\n setShowBtcLink(false);\n void refresh();\n onChange?.();\n }, [refresh, onChange]);\n\n if (error) {\n return (\n <div className={className} data-oc-li=\"\" style={panelStyle('warn')} role=\"alert\">\n <SectionLabel tone=\"warn\">§ identities · load deferred</SectionLabel>\n <p style={bodyStyle}>\n Couldn&apos;t reach the auth host ({error}). Linking is temporarily\n unavailable; your signed-in identity still works as normal.\n </p>\n <button type=\"button\" onClick={() => void refresh()} style={ghostBtnStyle(false)}>\n retry\n </button>\n </div>\n );\n }\n\n if (!resp) {\n return (\n <div className={className} data-oc-li=\"\" style={panelStyle()}>\n <span style={hintStyle}>loading…</span>\n </div>\n );\n }\n\n const linked = resp.linked ?? [];\n const emailRows = linked.filter((i) => i.kind === 'email');\n const btcRows = linked.filter((i) => i.kind === 'btc');\n const hasEmail = emailRows.some((i) => i.value);\n const hasBtc = btcRows.some((i) => i.value);\n const mergedFrom = resp.merged_from ?? [];\n\n return (\n <div className={className} data-oc-li=\"\" style={{ display: 'grid', gap: 16 }}>\n <div style={panelStyle()}>\n <SectionLabel>§ what this is</SectionLabel>\n <p style={bodyStyle}>\n These are the ways you can sign in to this account. An app you sign in to\n sees one only if you grant it. Linking a second one is also your way back\n in: lose one, and the other still signs you in.\n </p>\n </div>\n\n {mergedFrom.length > 0 && <ConsolidationPanel entries={mergedFrom} />}\n\n <div style={panelStyle('plain')} data-oc-li-rows=\"\">\n <IdentityGroup\n kind=\"email\"\n title=\"email\"\n rows={emailRows}\n authOrigin={authOrigin}\n onChange={afterChange}\n addCta={hasEmail ? null : 'link an email'}\n onAdd={() => {\n setShowEmailLink((v) => !v);\n setShowBtcLink(false);\n }}\n />\n <div style={{ borderTop: `1px solid ${V.border}` }} />\n <IdentityGroup\n kind=\"btc\"\n title=\"bitcoin address\"\n rows={btcRows}\n authOrigin={authOrigin}\n onChange={afterChange}\n addCta={hasBtc ? null : 'link a Bitcoin address'}\n onAdd={() => {\n setShowBtcLink((v) => !v);\n setShowEmailLink(false);\n }}\n />\n </div>\n\n {showEmailLink && (\n <EmailLinkForm\n authOrigin={authOrigin}\n onDone={afterChange}\n onCancel={() => setShowEmailLink(false)}\n />\n )}\n {showBtcLink && (\n <BtcLinkForm\n authOrigin={authOrigin}\n didOc={resp.did_oc ?? null}\n onDone={afterChange}\n onCancel={() => setShowBtcLink(false)}\n />\n )}\n\n {hasBtc && attestationLink && <AttestationLink />}\n </div>\n );\n}\n\n/* --- identity group (rows for one kind, or an empty-state add CTA) --- */\n\nfunction IdentityGroup({\n kind,\n title,\n rows,\n authOrigin,\n onChange,\n addCta,\n onAdd,\n}: {\n kind: 'email' | 'btc';\n title: string;\n rows: OcLinkedIdentity[];\n authOrigin: string;\n onChange: () => void;\n addCta: string | null;\n onAdd: () => void;\n}): React.ReactElement {\n if (rows.length === 0) {\n return (\n <div style={rowStyle}>\n <div>\n <div style={rowLabelStyle}>§ {title}</div>\n <div style={{ ...bodyStyle, marginTop: 4 }}>none linked</div>\n </div>\n {addCta && (\n <button type=\"button\" onClick={onAdd} style={primaryChipStyle}>\n + {addCta}\n </button>\n )}\n </div>\n );\n }\n return (\n <>\n {rows.map((r) => (\n <IdentityRow\n key={r.id}\n row={r}\n kind={kind}\n authOrigin={authOrigin}\n onChange={onChange}\n />\n ))}\n </>\n );\n}\n\n/* --- single identity row --- */\n\nfunction IdentityRow({\n row,\n kind,\n authOrigin,\n onChange,\n}: {\n row: OcLinkedIdentity;\n kind: 'email' | 'btc';\n authOrigin: string;\n onChange: () => void;\n}): React.ReactElement {\n const [shown, setShown] = React.useState(false);\n const [confirming, setConfirming] = React.useState(false);\n const [busy, setBusy] = React.useState(false);\n const [err, setErr] = React.useState<string | null>(null);\n\n const masked = row.value ? mask(row.value, kind) : '— pending recovery —';\n const display = shown && row.value ? row.value : masked;\n\n async function unlink(): Promise<void> {\n if (row.is_primary) return;\n setBusy(true);\n setErr(null);\n try {\n const r = await fetch(\n `${authOrigin}/api/auth/identities?id=${encodeURIComponent(row.id)}`,\n { method: 'DELETE', credentials: 'include' }\n );\n if (!r.ok) {\n const j = (await r.json().catch(() => ({}))) as { reason?: string };\n throw new Error(j.reason ?? `http_${r.status}`);\n }\n onChange();\n } catch (e) {\n setErr(e instanceof Error ? e.message : 'unlink failed');\n setBusy(false);\n setConfirming(false);\n }\n }\n\n return (\n <div style={rowStyle} data-oc-li-row={kind}>\n <div style={{ minWidth: 0, flex: 1 }}>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: '4px 12px' }}>\n <span style={rowLabelStyle}>\n § {kind === 'email' ? 'email' : 'bitcoin address'}\n </span>\n {row.is_primary && (\n <span style={{ ...rowLabelStyle, color: V.primary }}>\n primary · signup-time\n </span>\n )}\n </div>\n <div\n style={{\n marginTop: 4,\n color: V.foreground,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(12),\n wordBreak: 'break-all',\n }}\n >\n {display}\n </div>\n {row.verified_at && (\n <div style={{ ...hintStyle, marginTop: 4 }}>\n verified · {new Date(row.verified_at).toUTCString()}\n </div>\n )}\n {err && <ErrorLine>{err}</ErrorLine>}\n </div>\n <div style={{ display: 'flex', flexShrink: 0, gap: 8, flexWrap: 'wrap' }}>\n {row.value && (\n <button\n type=\"button\"\n onClick={() => setShown((s) => !s)}\n style={ghostBtnStyle(false)}\n >\n {shown ? 'hide' : 'show'}\n </button>\n )}\n {!row.is_primary && !confirming && (\n <button\n type=\"button\"\n onClick={() => setConfirming(true)}\n style={dangerBtnStyle(false)}\n >\n unlink\n </button>\n )}\n {!row.is_primary && confirming && (\n <>\n <button\n type=\"button\"\n onClick={() => void unlink()}\n disabled={busy}\n style={dangerBtnStyle(busy)}\n >\n {busy ? 'unlinking…' : 'confirm unlink'}\n </button>\n <button\n type=\"button\"\n onClick={() => setConfirming(false)}\n disabled={busy}\n style={ghostBtnStyle(busy)}\n >\n cancel\n </button>\n </>\n )}\n </div>\n </div>\n );\n}\n\n/* --- consolidated-accounts tombstone panel --- */\n\nfunction ConsolidationPanel({ entries }: { entries: MergedFromEntry[] }): React.ReactElement {\n return (\n <div style={panelStyle('success')} data-oc-li-consolidated=\"\">\n <SectionLabel tone=\"success\">\n § consolidated accounts · {entries.length}\n </SectionLabel>\n <p style={bodyStyle}>\n {entries.length === 1 ? 'A previous OC account' : 'Previous OC accounts'} you\n signed up with, folded into this one under dual proof of control. Historical\n activity is unioned into this account automatically;{' '}\n {entries.length === 1 ? 'it' : 'they'} can no longer sign in.\n </p>\n <ul style={{ listStyle: 'none', margin: '12px 0 0', padding: 0 }}>\n {entries.map((e) => (\n <li\n key={e.did_oc}\n style={{\n display: 'flex',\n flexWrap: 'wrap',\n gap: 8,\n padding: '8px 0',\n borderTop: `1px solid ${V.border}`,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(11),\n }}\n >\n <code style={{ color: V.foreground, wordBreak: 'break-all' }}>\n {e.did_oc}\n </code>\n <span style={{ ...hintStyle, marginLeft: 'auto' }}>\n created {new Date(e.created_at).toLocaleDateString()}\n </span>\n </li>\n ))}\n </ul>\n </div>\n );\n}\n\n/* --- portable-attestation pointer --- */\n\n/**\n * A thin pointer to the OC Attest builder. The portable, self-sovereign\n * form of identity linking is a standard OrangeCheck attestation — one\n * BIP-322 signature by the Bitcoin address binding it to its handles,\n * published to Nostr. That ceremony already lives at\n * `attest.ochk.io/create` (which pre-fills the address from the signed-in\n * session); this panel routes the user there rather than re-implementing\n * it. Shown once the account has a linked Bitcoin address to attest with.\n */\nfunction AttestationLink(): React.ReactElement {\n return (\n <div style={panelStyle()} data-oc-li-attest=\"\">\n <SectionLabel>§ portable attestation</SectionLabel>\n <p style={bodyStyle}>\n Make your identity links portable: publish a standard OrangeCheck\n attestation — one Bitcoin-wallet signature (BIP-322) binding your address\n to your handles (Nostr, GitHub, …). It is published to Nostr and verifies\n with zero trust in OrangeCheck, so it outlives any single service.\n </p>\n <a\n href=\"https://attest.ochk.io/create\"\n target=\"_blank\"\n rel=\"noreferrer\"\n style={{ ...primaryChipStyle, textDecoration: 'none', display: 'inline-block' }}\n data-oc-li-attest-link=\"\"\n >\n open the attestation builder ↗\n </a>\n </div>\n );\n}\n\n/* --- post-sign-in link prompt (used by OcSignIn) --- */\n\n/** Where the ways to sign in are managed: ochk.app, which replaces me.ochk.io's pages. */\nconst ACCOUNT_WAYS_URL = 'https://ochk.app/me/account#ways';\n\n/**\n * Shown by `<OcSignIn>` after a successful sign-in *when the user ticked\n * the \"also link my other identity\" checkbox on the sign-in form*. It runs\n * the complementary identity's link ceremony inline — BIP-322 for a\n * Bitcoin address, OTP for an email.\n *\n * Skipping is first-class: even though the user opted in at the form, they\n * may change their mind here — an explicit \"skip, I'll do this later\"\n * control exits cleanly and tells them where to link later. `onResolved`\n * (linked, or skipped) hands control back to OcSignIn's post-sign-in\n * navigation.\n */\nexport function LinkPromptStep({\n method,\n didOc,\n authOrigin,\n onResolved,\n}: {\n /** The complementary identity to link — the opposite of the sign-in method. */\n method: 'btc' | 'email';\n didOc: string;\n authOrigin: string;\n /** Called once the user has linked the identity, or skipped. */\n onResolved: () => void;\n}): React.ReactElement {\n const what = method === 'btc' ? 'a Bitcoin address' : 'an email';\n return (\n <div data-oc-linkprompt={method}>\n <div style={{ ...panelStyle('success'), marginBottom: 16 }}>\n <SectionLabel tone=\"success\">§ signed in</SectionLabel>\n <p style={{ ...bodyStyle, margin: 0 }}>\n You chose to also link {what}. Complete it below — or skip for now; it is\n entirely optional and changes nothing about the sign-in you just completed.\n </p>\n </div>\n {method === 'btc' ? (\n <BtcLinkForm\n authOrigin={authOrigin}\n didOc={didOc}\n onDone={onResolved}\n onCancel={onResolved}\n />\n ) : (\n <EmailLinkForm authOrigin={authOrigin} onDone={onResolved} onCancel={onResolved} />\n )}\n <div\n data-oc-linkprompt-skip=\"\"\n style={{ marginTop: 14, paddingTop: 14, borderTop: `1px solid ${V.border}` }}\n >\n <button type=\"button\" onClick={onResolved} style={ghostBtnStyle(false)}>\n skip — I&apos;ll do this later\n </button>\n <p style={{ ...hintStyle, marginTop: 8 }}>\n Changed your mind? Skipping continues you straight on — your sign-in is\n unaffected. You can link {what} anytime later from your account page,{' '}\n <a\n href={ACCOUNT_WAYS_URL}\n target=\"_blank\"\n rel=\"noreferrer\"\n style={{ color: 'inherit' }}\n data-oc-linkprompt-account=\"\"\n >\n ochk.app/me/account\n </a>\n .\n </p>\n </div>\n </div>\n );\n}\n\n/* --- email link form --- */\n\nfunction EmailLinkForm({\n authOrigin,\n onDone,\n onCancel,\n}: {\n authOrigin: string;\n onDone: () => void;\n onCancel: () => void;\n}): React.ReactElement {\n const [step, setStep] = React.useState<'email' | 'code'>('email');\n const [email, setEmail] = React.useState('');\n const [code, setCode] = React.useState('');\n const [token, setToken] = React.useState('');\n const [busy, setBusy] = React.useState(false);\n const [err, setErr] = React.useState<string | null>(null);\n // Set when the auth host reports the email is linked elsewhere — the\n // user can confirm a dual-proof transfer (session + verified OTP).\n const [transfer, setTransfer] = React.useState(false);\n\n async function start(): Promise<void> {\n setBusy(true);\n setErr(null);\n try {\n const r = await fetch(`${authOrigin}/api/auth/email-otp/start`, {\n method: 'POST',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ email: email.trim().toLowerCase() }),\n });\n const j = (await r.json()) as { ok?: boolean; token?: string; reason?: string };\n if (!r.ok || !j.token) throw new Error(j.reason ?? `http_${r.status}`);\n setToken(j.token);\n setStep('code');\n } catch (e) {\n setErr(e instanceof Error ? e.message : 'could not send code');\n } finally {\n setBusy(false);\n }\n }\n\n async function verify(confirmTransfer: boolean): Promise<void> {\n setBusy(true);\n setErr(null);\n try {\n const r = await fetch(`${authOrigin}/api/auth/link/email`, {\n method: 'POST',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n email: email.trim().toLowerCase(),\n code: code.trim(),\n token,\n ...(confirmTransfer ? { confirm_transfer: true } : {}),\n }),\n });\n const j = (await r.json()) as { ok?: boolean; reason?: string };\n if (r.status === 409 && j.reason === 'email_linked_elsewhere') {\n setTransfer(true);\n setBusy(false);\n return;\n }\n if (handleSudoRequired(j, { purpose: 'link an email' })) return;\n if (isSudoAccountMismatch(j)) {\n setErr(SUDO_ACCOUNT_MISMATCH_MESSAGE);\n setBusy(false);\n return;\n }\n if (!r.ok || !j.ok) throw new Error(j.reason ?? `http_${r.status}`);\n onDone();\n } catch (e) {\n setErr(e instanceof Error ? e.message : 'link failed');\n setBusy(false);\n }\n }\n\n return (\n <div style={panelStyle('accent')} data-oc-li-form=\"email\">\n <SectionLabel>§ link an email</SectionLabel>\n {step === 'email' ? (\n <>\n <input\n type=\"email\"\n autoComplete=\"email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n placeholder=\"you@example.com\"\n style={inputStyle}\n />\n {err && <ErrorLine>{err}</ErrorLine>}\n <FormButtons>\n <button\n type=\"button\"\n onClick={() => void start()}\n disabled={busy || email.trim().length < 5}\n style={primaryBtnStyle(busy || email.trim().length < 5)}\n >\n {busy ? 'sending…' : 'send code'}\n </button>\n <button type=\"button\" onClick={onCancel} style={ghostBtnStyle(busy)}>\n cancel\n </button>\n </FormButtons>\n </>\n ) : transfer ? (\n <TransferConfirm\n what=\"email\"\n busy={busy}\n onConfirm={() => void verify(true)}\n onCancel={onCancel}\n err={err}\n />\n ) : (\n <>\n <p style={bodyStyle}>Enter the 6-digit code sent to {email}.</p>\n <input\n type=\"text\"\n inputMode=\"numeric\"\n autoComplete=\"one-time-code\"\n maxLength={6}\n value={code}\n onChange={(e) => setCode(e.target.value.replace(/\\D/g, '').slice(0, 6))}\n placeholder=\"123456\"\n style={{ ...inputStyle, letterSpacing: '0.3em' }}\n />\n {err && <ErrorLine>{err}</ErrorLine>}\n <FormButtons>\n <button\n type=\"button\"\n onClick={() => void verify(false)}\n disabled={busy || code.length !== 6}\n style={primaryBtnStyle(busy || code.length !== 6)}\n >\n {busy ? 'linking…' : 'verify + link'}\n </button>\n <button\n type=\"button\"\n onClick={() => setStep('email')}\n disabled={busy}\n style={ghostBtnStyle(busy)}\n >\n back\n </button>\n </FormButtons>\n </>\n )}\n </div>\n );\n}\n\n/* --- btc link form --- */\n\nfunction BtcLinkForm({\n authOrigin,\n didOc,\n onDone,\n onCancel,\n}: {\n authOrigin: string;\n didOc: string | null;\n onDone: () => void;\n onCancel: () => void;\n}): React.ReactElement {\n const [step, setStep] = React.useState<'request' | 'sign'>('request');\n const [addr, setAddr] = React.useState('');\n const [challenge, setChallenge] = React.useState('');\n const [busy, setBusy] = React.useState(false);\n const [err, setErr] = React.useState<string | null>(null);\n // Holds the signature awaiting transfer confirmation when the\n // address is already linked to another account.\n const [pendingSig, setPendingSig] = React.useState<string | null>(null);\n\n async function getChallenge(): Promise<void> {\n if (!didOc) {\n setErr('session not loaded yet · refresh and retry');\n return;\n }\n setBusy(true);\n setErr(null);\n try {\n // /api/challenge is a public primitive — no session needed.\n // audience MUST be the caller's did_oc: the link endpoint\n // verifies expectedAudience=session.did_oc, so a signature\n // obtained here can't be replayed against another account.\n const r = await fetch(\n `${authOrigin}/api/challenge?addr=${encodeURIComponent(addr)}` +\n `&purpose=link&audience=${encodeURIComponent(didOc)}`\n );\n const j = (await r.json()) as { message?: string; reason?: string };\n if (!r.ok || !j.message) throw new Error(j.reason ?? `http_${r.status}`);\n setChallenge(j.message);\n setStep('sign');\n } catch (e) {\n setErr(e instanceof Error ? e.message : 'challenge failed');\n } finally {\n setBusy(false);\n }\n }\n\n async function postLink(sig: string, confirmTransfer: boolean): Promise<void> {\n setBusy(true);\n setErr(null);\n try {\n const r = await fetch(`${authOrigin}/api/auth/link/btc`, {\n method: 'POST',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n message: challenge,\n signature: sig,\n ...(confirmTransfer ? { confirm_transfer: true } : {}),\n }),\n });\n const j = (await r.json()) as { ok?: boolean; reason?: string };\n if (r.status === 409 && j.reason === 'address_linked_elsewhere') {\n setPendingSig(sig);\n setBusy(false);\n return;\n }\n if (handleSudoRequired(j, { purpose: 'link a Bitcoin address' })) return;\n if (isSudoAccountMismatch(j)) {\n setErr(SUDO_ACCOUNT_MISMATCH_MESSAGE);\n setBusy(false);\n return;\n }\n if (!r.ok || !j.ok) throw new Error(j.reason ?? `http_${r.status}`);\n onDone();\n } catch (e) {\n setErr(e instanceof Error ? e.message : 'link failed');\n setBusy(false);\n }\n }\n\n return (\n <div style={panelStyle('accent')} data-oc-li-form=\"btc\">\n <SectionLabel>§ link a Bitcoin address</SectionLabel>\n {step === 'request' ? (\n <>\n <p style={bodyStyle}>\n Enter a Bitcoin address you control. We generate a challenge and your\n wallet signs it (BIP-322).\n </p>\n <input\n type=\"text\"\n value={addr}\n onChange={(e) => setAddr(e.target.value.trim())}\n placeholder=\"bc1q…\"\n autoCapitalize=\"none\"\n spellCheck={false}\n style={{ ...inputStyle, fontFamily: SIGNIN_FONT }}\n />\n {err && <ErrorLine>{err}</ErrorLine>}\n <FormButtons>\n <button\n type=\"button\"\n onClick={() => void getChallenge()}\n disabled={busy || addr.length < 14}\n style={primaryBtnStyle(busy || addr.length < 14)}\n >\n {busy ? 'requesting…' : 'get challenge'}\n </button>\n <button type=\"button\" onClick={onCancel} style={ghostBtnStyle(busy)}>\n cancel\n </button>\n </FormButtons>\n </>\n ) : pendingSig ? (\n <TransferConfirm\n what=\"bitcoin address\"\n busy={busy}\n onConfirm={() => void postLink(pendingSig, true)}\n onCancel={onCancel}\n err={err}\n />\n ) : (\n <>\n <p style={bodyStyle}>\n Sign the challenge with the wallet that controls{' '}\n <span style={{ fontFamily: SIGNIN_FONT, wordBreak: 'break-all' }}>{addr}</span>.\n </p>\n <LazyWalletButton\n address={addr}\n message={challenge}\n showManual\n layout=\"list\"\n heading={null}\n onSigned={(sig: string) => void postLink(sig, false)}\n onError={(e: { message?: string }) =>\n setErr(e?.message ?? 'wallet rejected')\n }\n />\n {err && <ErrorLine>{err}</ErrorLine>}\n <FormButtons>\n <button\n type=\"button\"\n onClick={() => {\n setStep('request');\n setErr(null);\n }}\n disabled={busy}\n style={ghostBtnStyle(busy)}\n >\n back\n </button>\n </FormButtons>\n </>\n )}\n </div>\n );\n}\n\n/* --- transfer-under-dual-proof confirm --- */\n\nfunction TransferConfirm({\n what,\n busy,\n onConfirm,\n onCancel,\n err,\n}: {\n what: string;\n busy: boolean;\n onConfirm: () => void;\n onCancel: () => void;\n err: string | null;\n}): React.ReactElement {\n return (\n <>\n <p style={bodyStyle}>\n This {what} is already linked to another OrangeCheck account. You&apos;ve just\n proven control of it — you can transfer the link to this account under dual proof\n of control. The other account loses this sign-in path; its history stays\n verifiable. Continue?\n </p>\n {err && <ErrorLine>{err}</ErrorLine>}\n <FormButtons>\n <button\n type=\"button\"\n onClick={onConfirm}\n disabled={busy}\n style={dangerBtnStyle(busy)}\n >\n {busy ? 'transferring…' : 'transfer link here'}\n </button>\n <button type=\"button\" onClick={onCancel} disabled={busy} style={ghostBtnStyle(busy)}>\n cancel\n </button>\n </FormButtons>\n </>\n );\n}\n\n/* --- lazy wallet button · wallet-adapter loads on demand, not with the page --- */\n\ninterface WalletButtonProps {\n address: string;\n message: string;\n showManual?: boolean;\n layout?: string;\n heading?: React.ReactNode;\n onSigned: (sig: string) => void;\n onError: (err: { message?: string }, walletId?: string) => void;\n}\n\nexport function LazyWalletButton(props: WalletButtonProps): React.ReactElement {\n const [Comp, setComp] = React.useState<React.ComponentType<WalletButtonProps> | null>(null);\n const [failed, setFailed] = React.useState(false);\n\n React.useEffect(() => {\n let alive = true;\n // A bundler resolves this specifier at build time, so wallet-adapter\n // is a real dependency; only the chunk is deferred.\n import('@orangecheck/wallet-adapter/react')\n .then((m) => {\n if (alive) {\n setComp(\n () => (m as { OcWalletButton: React.ComponentType<WalletButtonProps> })\n .OcWalletButton\n );\n }\n })\n .catch(() => {\n if (alive) setFailed(true);\n });\n return () => {\n alive = false;\n };\n }, []);\n\n if (failed) {\n return (\n <ErrorLine>\n could not load wallet support · refresh to try again\n </ErrorLine>\n );\n }\n if (!Comp) return <span style={hintStyle}>loading wallet options…</span>;\n return <Comp {...props} />;\n}\n\n/* --- helpers --- */\n\nfunction mask(value: string, kind: 'email' | 'btc'): string {\n if (kind === 'email') {\n const [user, host] = value.split('@');\n if (!user || !host) return '••••';\n const m =\n user.length <= 2\n ? '•'.repeat(user.length)\n : user[0] + '•'.repeat(user.length - 2) + user.slice(-1);\n return `${m}@${host}`;\n }\n if (value.length <= 12) return '•'.repeat(value.length);\n return `${value.slice(0, 6)}…${value.slice(-4)}`;\n}\n\n/* --- presentational atoms · also used by bond.tsx; not re-exported from index --- */\n\nexport function SectionLabel({\n children,\n tone = 'primary',\n}: {\n children: React.ReactNode;\n tone?: 'primary' | 'success' | 'warn';\n}): React.ReactElement {\n const color = tone === 'success' ? V.success : tone === 'warn' ? V.warn : V.primary;\n return (\n <div\n style={{\n color,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(10),\n letterSpacing: signinTracking('0.18em'),\n textTransform: SIGNIN_CASE,\n marginBottom: 8,\n }}\n >\n {children}\n </div>\n );\n}\n\nexport function ErrorLine({ children }: { children: React.ReactNode }): React.ReactElement {\n return (\n <p\n role=\"alert\"\n style={{\n margin: '8px 0 0',\n color: V.destructive,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(11),\n }}\n >\n {children}\n </p>\n );\n}\n\nexport function FormButtons({ children }: { children: React.ReactNode }): React.ReactElement {\n return (\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, marginTop: 12 }}>{children}</div>\n );\n}\n\n/* --- style tokens (CSS-variable-themed, same approach as OcSignIn) --- */\n\n\nexport const V = {\n primary: 'var(--primary, #f97316)',\n foreground: 'var(--foreground, #fafafa)',\n muted: 'var(--muted-foreground, #a1a1aa)',\n background: 'var(--background, #0a0a0a)',\n card: 'var(--card, #111113)',\n border: 'var(--border, #27272a)',\n destructive: 'var(--destructive, #ef4444)',\n success: 'var(--success, #22c55e)',\n warn: 'var(--warning, #eab308)',\n} as const;\n\nexport function panelStyle(tone: 'plain' | 'accent' | 'success' | 'warn' | 'default' = 'default'): React.CSSProperties {\n const base: React.CSSProperties = {\n background: V.card,\n border: `1px solid ${V.border}`,\n padding: '1.25rem',\n };\n if (tone === 'plain') return { ...base, padding: 0 };\n if (tone === 'accent') return { ...base, borderColor: V.primary, background: 'var(--card, #111113)' };\n if (tone === 'success') return { ...base, borderColor: V.success };\n if (tone === 'warn') return { ...base, borderColor: V.warn };\n return base;\n}\n\nexport const bodyStyle: React.CSSProperties = {\n color: V.muted,\n fontSize: 13,\n lineHeight: 1.55,\n margin: '0 0 12px',\n};\n\nexport const hintStyle: React.CSSProperties = {\n color: V.muted,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(10.5),\n opacity: 0.8,\n};\n\nconst rowStyle: React.CSSProperties = {\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: 12,\n padding: '1rem',\n};\n\nconst rowLabelStyle: React.CSSProperties = {\n color: V.foreground,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(10.5),\n letterSpacing: signinTracking('0.12em'),\n textTransform: SIGNIN_CASE,\n opacity: 0.8,\n};\n\nexport const inputStyle: React.CSSProperties = {\n width: '100%',\n padding: '0.55rem 0.75rem',\n background: V.background,\n color: V.foreground,\n border: `1px solid ${V.border}`,\n borderRadius: signinRadius(6),\n fontFamily: SIGNIN_FONT,\n // 16px — never lower · suppresses iOS Safari focus auto-zoom.\n fontSize: 16,\n outline: 'none',\n boxSizing: 'border-box',\n};\n\nfunction btnBase(disabled: boolean): React.CSSProperties {\n return {\n padding: '0.5rem 0.875rem',\n borderRadius: SIGNIN_BUTTON_RADIUS,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(10.5),\n letterSpacing: signinTracking('0.12em'),\n textTransform: SIGNIN_CASE,\n cursor: disabled ? 'not-allowed' : 'pointer',\n opacity: disabled ? 0.5 : 1,\n };\n}\n\nexport function primaryBtnStyle(disabled: boolean): React.CSSProperties {\n return {\n ...btnBase(disabled),\n background: V.primary,\n color: 'var(--primary-foreground, #0a0a0a)',\n border: `1px solid ${V.primary}`,\n fontWeight: 600,\n };\n}\n\nexport function ghostBtnStyle(disabled: boolean): React.CSSProperties {\n return {\n ...btnBase(disabled),\n background: V.background,\n color: V.foreground,\n border: `1px solid ${V.border}`,\n };\n}\n\nexport function dangerBtnStyle(disabled: boolean): React.CSSProperties {\n return {\n ...btnBase(disabled),\n background: 'transparent',\n color: V.destructive,\n border: `1px solid ${V.destructive}`,\n };\n}\n\nconst primaryChipStyle: React.CSSProperties = {\n ...btnBase(false),\n background: 'transparent',\n color: V.primary,\n border: `1px solid ${V.primary}`,\n};\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/linked-identities.tsx"],"sourcesContent":["'use client';\n\n/**\n * @orangecheck/auth-client · OcLinkedIdentities\n *\n * The shared linked-identities management surface for every `X.ochk.io`\n * consumer. A signed-in user sees their linked email + Bitcoin address,\n * can reveal/hide each raw value, unlink a non-primary identity, and\n * link a new one. Linking a Bitcoin address that's already on another\n * OC account offers a dual-proof transfer; the same is now true for\n * email (auth host F4). Consolidated (`merged_from`) accounts render in\n * a tombstone panel.\n *\n * This generalizes me.ochk.io's bespoke `LinkedIdentitiesPanel` so\n * linking is no longer a single-product, one-tab feature — any family\n * site can drop in `<OcLinkedIdentities />`. See AUTH-PLAN.md §5.\n *\n * All endpoints live on the auth host (`ochk.io`); calls are\n * cross-origin with `credentials: 'include'` and rely on the auth\n * host's family-CORS — the same posture `OcSignIn` uses. No local\n * proxy route is needed (AUTH-AUDIT.md F6: direct-to-host is the\n * intended design).\n *\n * Style: inline, CSS-variable-themed (same approach as `OcSignIn`), so\n * the component carries zero CSS dependency and themes to each site.\n * Every block carries a `data-oc-li-*` attribute so a site may\n * override via its own stylesheet.\n */\n\nimport * as React from 'react';\nimport {\n handleSudoRequired,\n isSudoAccountMismatch,\n SUDO_ACCOUNT_MISMATCH_MESSAGE,\n} from './sudo';\n\nimport {\n SIGNIN_BUTTON_RADIUS,\n SIGNIN_CASE,\n SIGNIN_FONT,\n signinRadius,\n signinSize,\n signinTracking,\n} from './appearance';\nimport { tabSessionHeader } from './tab-session';\n\nconst DEFAULT_AUTH_ORIGIN = 'https://ochk.io';\n\n/* --- types --- */\n\nexport interface OcLinkedIdentity {\n id: string;\n kind: 'email' | 'btc';\n /** Raw value · null when the identity is pending recovery. */\n value: string | null;\n verified_at: string | null;\n verification_method: 'email-otp' | 'bip322' | null;\n /** The signup-time identity for its kind · cannot be unlinked here. */\n is_primary: boolean;\n}\n\ninterface MergedFromEntry {\n did_oc: string;\n created_at: string;\n last_signed_in_at: string | null;\n}\n\ninterface IdentitiesResp {\n ok: boolean;\n account_id?: string;\n did_oc?: string | null;\n linked?: OcLinkedIdentity[];\n merged_from?: MergedFromEntry[];\n reason?: string;\n}\n\n/**\n * Fetch the signed-in user's linked identities from the auth host —\n * the same data `<OcLinkedIdentities>` renders, exposed as a plain\n * function for surfaces that need the list without the full\n * management UI (notably `<OcAccountMenu>`'s badge-identity promote\n * list). Returns `[]` when anonymous; throws on network / server\n * error so the caller can show a deferred state.\n */\nexport async function fetchOcLinkedIdentities(opts?: {\n authOrigin?: string;\n}): Promise<OcLinkedIdentity[]> {\n const authOrigin = opts?.authOrigin ?? DEFAULT_AUTH_ORIGIN;\n // Per-tab pinning: forward the tab's session JWT so the host resolves\n // the SAME account this tab displays (its chip reads /api/auth/me\n // tab-aware). Without this, a pinned tab's show-as list is the shared\n // cookie account's identities — a menu that disagrees with itself.\n // Unpinned tabs send no header → unchanged cookie-default behavior.\n const r = await fetch(`${authOrigin}/api/auth/identities`, {\n credentials: 'include',\n headers: { ...tabSessionHeader() },\n });\n if (r.status === 401) return [];\n if (!r.ok) throw new Error(`identities fetch failed: http_${r.status}`);\n const body = (await r.json()) as IdentitiesResp;\n return body.linked ?? [];\n}\n\nexport interface OcLinkedIdentitiesProps {\n /** Auth host origin. Defaults to `https://ochk.io`. */\n authOrigin?: string;\n /** Outer container className. */\n className?: string;\n /** Called after every successful link / unlink / transfer. */\n onChange?: () => void;\n /**\n * Show the \"portable attestation\" card that links out to attest.ochk.io.\n * Defaults to `true`. Pass `false` on a page that already hosts its own\n * attestation builder (ochk.io's dashboard), or the same action is offered\n * twice, one panel apart.\n */\n attestationLink?: boolean;\n}\n\n/* --- main --- */\n\nexport function OcLinkedIdentities({\n authOrigin = DEFAULT_AUTH_ORIGIN,\n className,\n onChange,\n attestationLink = true,\n}: OcLinkedIdentitiesProps): React.ReactElement {\n const [resp, setResp] = React.useState<IdentitiesResp | null>(null);\n const [error, setError] = React.useState<string | null>(null);\n const [showEmailLink, setShowEmailLink] = React.useState(false);\n const [showBtcLink, setShowBtcLink] = React.useState(false);\n\n const refresh = React.useCallback(async () => {\n setError(null);\n try {\n const r = await fetch(`${authOrigin}/api/auth/identities`, {\n credentials: 'include',\n headers: { ...tabSessionHeader() },\n });\n if (!r.ok) {\n if (r.status === 401) {\n setError('not signed in');\n return;\n }\n throw new Error(`http_${r.status}`);\n }\n setResp((await r.json()) as IdentitiesResp);\n } catch (e) {\n setError(e instanceof Error ? e.message : 'load failed');\n }\n }, [authOrigin]);\n\n React.useEffect(() => {\n void refresh();\n }, [refresh]);\n\n const afterChange = React.useCallback(() => {\n setShowEmailLink(false);\n setShowBtcLink(false);\n void refresh();\n onChange?.();\n }, [refresh, onChange]);\n\n if (error) {\n return (\n <div className={className} data-oc-li=\"\" style={panelStyle('warn')} role=\"alert\">\n <SectionLabel tone=\"warn\">§ identities · load deferred</SectionLabel>\n <p style={bodyStyle}>\n Couldn&apos;t reach the auth host ({error}). Linking is temporarily\n unavailable; your signed-in identity still works as normal.\n </p>\n <button type=\"button\" onClick={() => void refresh()} style={ghostBtnStyle(false)}>\n retry\n </button>\n </div>\n );\n }\n\n if (!resp) {\n return (\n <div className={className} data-oc-li=\"\" style={panelStyle()}>\n <span style={hintStyle}>loading…</span>\n </div>\n );\n }\n\n const linked = resp.linked ?? [];\n const emailRows = linked.filter((i) => i.kind === 'email');\n const btcRows = linked.filter((i) => i.kind === 'btc');\n const hasEmail = emailRows.some((i) => i.value);\n const hasBtc = btcRows.some((i) => i.value);\n const mergedFrom = resp.merged_from ?? [];\n\n return (\n <div className={className} data-oc-li=\"\" style={{ display: 'grid', gap: 16 }}>\n <div style={panelStyle()}>\n <SectionLabel>§ what this is</SectionLabel>\n <p style={bodyStyle}>\n These are the ways you can sign in to this account. An app you sign in to\n sees one only if you grant it. Linking a second one is also your way back\n in: lose one, and the other still signs you in.\n </p>\n </div>\n\n {mergedFrom.length > 0 && <ConsolidationPanel entries={mergedFrom} />}\n\n <div style={panelStyle('plain')} data-oc-li-rows=\"\">\n <IdentityGroup\n kind=\"email\"\n title=\"email\"\n rows={emailRows}\n authOrigin={authOrigin}\n onChange={afterChange}\n addCta={hasEmail ? null : 'link an email'}\n onAdd={() => {\n setShowEmailLink((v) => !v);\n setShowBtcLink(false);\n }}\n />\n <div style={{ borderTop: `1px solid ${V.border}` }} />\n <IdentityGroup\n kind=\"btc\"\n title=\"bitcoin address\"\n rows={btcRows}\n authOrigin={authOrigin}\n onChange={afterChange}\n addCta={hasBtc ? null : 'link a Bitcoin address'}\n onAdd={() => {\n setShowBtcLink((v) => !v);\n setShowEmailLink(false);\n }}\n />\n </div>\n\n {showEmailLink && (\n <EmailLinkForm\n authOrigin={authOrigin}\n onDone={afterChange}\n onCancel={() => setShowEmailLink(false)}\n />\n )}\n {showBtcLink && (\n <BtcLinkForm\n authOrigin={authOrigin}\n didOc={resp.did_oc ?? null}\n onDone={afterChange}\n onCancel={() => setShowBtcLink(false)}\n />\n )}\n\n {hasBtc && attestationLink && <AttestationLink />}\n </div>\n );\n}\n\n/* --- identity group (rows for one kind, or an empty-state add CTA) --- */\n\nfunction IdentityGroup({\n kind,\n title,\n rows,\n authOrigin,\n onChange,\n addCta,\n onAdd,\n}: {\n kind: 'email' | 'btc';\n title: string;\n rows: OcLinkedIdentity[];\n authOrigin: string;\n onChange: () => void;\n addCta: string | null;\n onAdd: () => void;\n}): React.ReactElement {\n if (rows.length === 0) {\n return (\n <div style={rowStyle}>\n <div>\n <div style={rowLabelStyle}>§ {title}</div>\n <div style={{ ...bodyStyle, marginTop: 4 }}>none linked</div>\n </div>\n {addCta && (\n <button type=\"button\" onClick={onAdd} style={primaryChipStyle}>\n + {addCta}\n </button>\n )}\n </div>\n );\n }\n return (\n <>\n {rows.map((r) => (\n <IdentityRow\n key={r.id}\n row={r}\n kind={kind}\n authOrigin={authOrigin}\n onChange={onChange}\n />\n ))}\n </>\n );\n}\n\n/* --- single identity row --- */\n\nfunction IdentityRow({\n row,\n kind,\n authOrigin,\n onChange,\n}: {\n row: OcLinkedIdentity;\n kind: 'email' | 'btc';\n authOrigin: string;\n onChange: () => void;\n}): React.ReactElement {\n const [shown, setShown] = React.useState(false);\n const [confirming, setConfirming] = React.useState(false);\n const [busy, setBusy] = React.useState(false);\n const [err, setErr] = React.useState<string | null>(null);\n\n const masked = row.value ? mask(row.value, kind) : '— pending recovery —';\n const display = shown && row.value ? row.value : masked;\n\n async function unlink(): Promise<void> {\n if (row.is_primary) return;\n setBusy(true);\n setErr(null);\n try {\n const r = await fetch(\n `${authOrigin}/api/auth/identities?id=${encodeURIComponent(row.id)}`,\n { method: 'DELETE', credentials: 'include' }\n );\n if (!r.ok) {\n const j = (await r.json().catch(() => ({}))) as { reason?: string };\n throw new Error(j.reason ?? `http_${r.status}`);\n }\n onChange();\n } catch (e) {\n setErr(e instanceof Error ? e.message : 'unlink failed');\n setBusy(false);\n setConfirming(false);\n }\n }\n\n return (\n <div style={rowStyle} data-oc-li-row={kind}>\n <div style={{ minWidth: 0, flex: 1 }}>\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: '4px 12px' }}>\n <span style={rowLabelStyle}>\n § {kind === 'email' ? 'email' : 'bitcoin address'}\n </span>\n {row.is_primary && (\n <span style={{ ...rowLabelStyle, color: V.primary }}>\n primary · signup-time\n </span>\n )}\n </div>\n <div\n style={{\n marginTop: 4,\n color: V.foreground,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(12),\n wordBreak: 'break-all',\n }}\n >\n {display}\n </div>\n {row.verified_at && (\n <div style={{ ...hintStyle, marginTop: 4 }}>\n verified · {new Date(row.verified_at).toUTCString()}\n </div>\n )}\n {err && <ErrorLine>{err}</ErrorLine>}\n </div>\n <div style={{ display: 'flex', flexShrink: 0, gap: 8, flexWrap: 'wrap' }}>\n {row.value && (\n <button\n type=\"button\"\n onClick={() => setShown((s) => !s)}\n style={ghostBtnStyle(false)}\n >\n {shown ? 'hide' : 'show'}\n </button>\n )}\n {!row.is_primary && !confirming && (\n <button\n type=\"button\"\n onClick={() => setConfirming(true)}\n style={dangerBtnStyle(false)}\n >\n unlink\n </button>\n )}\n {!row.is_primary && confirming && (\n <>\n <button\n type=\"button\"\n onClick={() => void unlink()}\n disabled={busy}\n style={dangerBtnStyle(busy)}\n >\n {busy ? 'unlinking…' : 'confirm unlink'}\n </button>\n <button\n type=\"button\"\n onClick={() => setConfirming(false)}\n disabled={busy}\n style={ghostBtnStyle(busy)}\n >\n cancel\n </button>\n </>\n )}\n </div>\n </div>\n );\n}\n\n/* --- consolidated-accounts tombstone panel --- */\n\nfunction ConsolidationPanel({ entries }: { entries: MergedFromEntry[] }): React.ReactElement {\n return (\n <div style={panelStyle('success')} data-oc-li-consolidated=\"\">\n <SectionLabel tone=\"success\">\n § consolidated accounts · {entries.length}\n </SectionLabel>\n <p style={bodyStyle}>\n {entries.length === 1 ? 'A previous OC account' : 'Previous OC accounts'} you\n signed up with, folded into this one under dual proof of control. Historical\n activity is unioned into this account automatically;{' '}\n {entries.length === 1 ? 'it' : 'they'} can no longer sign in.\n </p>\n <ul style={{ listStyle: 'none', margin: '12px 0 0', padding: 0 }}>\n {entries.map((e) => (\n <li\n key={e.did_oc}\n style={{\n display: 'flex',\n flexWrap: 'wrap',\n gap: 8,\n padding: '8px 0',\n borderTop: `1px solid ${V.border}`,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(11),\n }}\n >\n <code style={{ color: V.foreground, wordBreak: 'break-all' }}>\n {e.did_oc}\n </code>\n <span style={{ ...hintStyle, marginLeft: 'auto' }}>\n created {new Date(e.created_at).toLocaleDateString()}\n </span>\n </li>\n ))}\n </ul>\n </div>\n );\n}\n\n/* --- portable-attestation pointer --- */\n\n/**\n * A thin pointer to the OC Attest builder. The portable, self-sovereign\n * form of identity linking is a standard OrangeCheck attestation — one\n * BIP-322 signature by the Bitcoin address binding it to its handles,\n * published to Nostr. That ceremony already lives at\n * `attest.ochk.io/create` (which pre-fills the address from the signed-in\n * session); this panel routes the user there rather than re-implementing\n * it. Shown once the account has a linked Bitcoin address to attest with.\n */\nfunction AttestationLink(): React.ReactElement {\n return (\n <div style={panelStyle()} data-oc-li-attest=\"\">\n <SectionLabel>§ portable attestation</SectionLabel>\n <p style={bodyStyle}>\n Make your identity links portable: publish a standard OrangeCheck\n attestation — one Bitcoin-wallet signature (BIP-322) binding your address\n to your handles (Nostr, GitHub, …). It is published to Nostr and verifies\n with zero trust in OrangeCheck, so it outlives any single service.\n </p>\n <a\n href=\"https://attest.ochk.io/create\"\n target=\"_blank\"\n rel=\"noreferrer\"\n style={{ ...primaryChipStyle, textDecoration: 'none', display: 'inline-block' }}\n data-oc-li-attest-link=\"\"\n >\n open the attestation builder ↗\n </a>\n </div>\n );\n}\n\n/* --- post-sign-in link prompt (used by OcSignIn) --- */\n\n/** Where the ways to sign in are managed: ochk.app, which replaces me.ochk.io's pages. */\nconst ACCOUNT_WAYS_URL = 'https://ochk.app/me/account#ways';\n\n/**\n * Shown by `<OcSignIn>` after a successful sign-in *when the user ticked\n * the \"also link my other identity\" checkbox on the sign-in form*. It runs\n * the complementary identity's link ceremony inline — BIP-322 for a\n * Bitcoin address, OTP for an email.\n *\n * Skipping is first-class: even though the user opted in at the form, they\n * may change their mind here — an explicit \"skip, I'll do this later\"\n * control exits cleanly and tells them where to link later. `onResolved`\n * (linked, or skipped) hands control back to OcSignIn's post-sign-in\n * navigation.\n */\nexport function LinkPromptStep({\n method,\n didOc,\n authOrigin,\n onResolved,\n}: {\n /** The complementary identity to link — the opposite of the sign-in method. */\n method: 'btc' | 'email';\n didOc: string;\n authOrigin: string;\n /** Called once the user has linked the identity, or skipped. */\n onResolved: () => void;\n}): React.ReactElement {\n const what = method === 'btc' ? 'a Bitcoin address' : 'an email';\n return (\n <div data-oc-linkprompt={method}>\n <div style={{ ...panelStyle('success'), marginBottom: 16 }}>\n <SectionLabel tone=\"success\">§ signed in</SectionLabel>\n <p style={{ ...bodyStyle, margin: 0 }}>\n You chose to also link {what}. Complete it below — or skip for now; it is\n entirely optional and changes nothing about the sign-in you just completed.\n </p>\n </div>\n {method === 'btc' ? (\n <BtcLinkForm\n authOrigin={authOrigin}\n didOc={didOc}\n onDone={onResolved}\n onCancel={onResolved}\n />\n ) : (\n <EmailLinkForm authOrigin={authOrigin} onDone={onResolved} onCancel={onResolved} />\n )}\n <div\n data-oc-linkprompt-skip=\"\"\n style={{ marginTop: 14, paddingTop: 14, borderTop: `1px solid ${V.border}` }}\n >\n <button type=\"button\" onClick={onResolved} style={ghostBtnStyle(false)}>\n skip — I&apos;ll do this later\n </button>\n <p style={{ ...hintStyle, marginTop: 8 }}>\n Changed your mind? Skipping continues you straight on — your sign-in is\n unaffected. You can link {what} anytime later from your account page,{' '}\n <a\n href={ACCOUNT_WAYS_URL}\n target=\"_blank\"\n rel=\"noreferrer\"\n style={{ color: 'inherit' }}\n data-oc-linkprompt-account=\"\"\n >\n ochk.app/me/account\n </a>\n .\n </p>\n </div>\n </div>\n );\n}\n\n/* --- email link form --- */\n\nfunction EmailLinkForm({\n authOrigin,\n onDone,\n onCancel,\n}: {\n authOrigin: string;\n onDone: () => void;\n onCancel: () => void;\n}): React.ReactElement {\n const [step, setStep] = React.useState<'email' | 'code'>('email');\n const [email, setEmail] = React.useState('');\n const [code, setCode] = React.useState('');\n const [token, setToken] = React.useState('');\n const [busy, setBusy] = React.useState(false);\n const [err, setErr] = React.useState<string | null>(null);\n // Set when the auth host reports the email is linked elsewhere — the\n // user can confirm a dual-proof transfer (session + verified OTP).\n const [transfer, setTransfer] = React.useState(false);\n\n async function start(): Promise<void> {\n setBusy(true);\n setErr(null);\n try {\n const r = await fetch(`${authOrigin}/api/auth/email-otp/start`, {\n method: 'POST',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ email: email.trim().toLowerCase() }),\n });\n const j = (await r.json()) as { ok?: boolean; token?: string; reason?: string };\n if (!r.ok || !j.token) throw new Error(j.reason ?? `http_${r.status}`);\n setToken(j.token);\n setStep('code');\n } catch (e) {\n setErr(e instanceof Error ? e.message : 'could not send code');\n } finally {\n setBusy(false);\n }\n }\n\n async function verify(confirmTransfer: boolean): Promise<void> {\n setBusy(true);\n setErr(null);\n try {\n const r = await fetch(`${authOrigin}/api/auth/link/email`, {\n method: 'POST',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n email: email.trim().toLowerCase(),\n code: code.trim(),\n token,\n ...(confirmTransfer ? { confirm_transfer: true } : {}),\n }),\n });\n const j = (await r.json()) as { ok?: boolean; reason?: string };\n if (r.status === 409 && j.reason === 'email_linked_elsewhere') {\n setTransfer(true);\n setBusy(false);\n return;\n }\n if (handleSudoRequired(j, { purpose: 'link an email' })) return;\n if (isSudoAccountMismatch(j)) {\n setErr(SUDO_ACCOUNT_MISMATCH_MESSAGE);\n setBusy(false);\n return;\n }\n if (!r.ok || !j.ok) throw new Error(j.reason ?? `http_${r.status}`);\n onDone();\n } catch (e) {\n setErr(e instanceof Error ? e.message : 'link failed');\n setBusy(false);\n }\n }\n\n return (\n <div style={panelStyle('accent')} data-oc-li-form=\"email\">\n <SectionLabel>§ link an email</SectionLabel>\n {step === 'email' ? (\n <>\n <input\n type=\"email\"\n autoComplete=\"email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n placeholder=\"you@example.com\"\n style={inputStyle}\n />\n {err && <ErrorLine>{err}</ErrorLine>}\n <FormButtons>\n <button\n type=\"button\"\n onClick={() => void start()}\n disabled={busy || email.trim().length < 5}\n style={primaryBtnStyle(busy || email.trim().length < 5)}\n >\n {busy ? 'sending…' : 'send code'}\n </button>\n <button type=\"button\" onClick={onCancel} style={ghostBtnStyle(busy)}>\n cancel\n </button>\n </FormButtons>\n </>\n ) : transfer ? (\n <TransferConfirm\n what=\"email\"\n busy={busy}\n onConfirm={() => void verify(true)}\n onCancel={onCancel}\n err={err}\n />\n ) : (\n <>\n <p style={bodyStyle}>Enter the 6-digit code sent to {email}.</p>\n <input\n type=\"text\"\n inputMode=\"numeric\"\n autoComplete=\"one-time-code\"\n maxLength={6}\n value={code}\n onChange={(e) => setCode(e.target.value.replace(/\\D/g, '').slice(0, 6))}\n placeholder=\"123456\"\n style={{ ...inputStyle, letterSpacing: '0.3em' }}\n />\n {err && <ErrorLine>{err}</ErrorLine>}\n <FormButtons>\n <button\n type=\"button\"\n onClick={() => void verify(false)}\n disabled={busy || code.length !== 6}\n style={primaryBtnStyle(busy || code.length !== 6)}\n >\n {busy ? 'linking…' : 'verify + link'}\n </button>\n <button\n type=\"button\"\n onClick={() => setStep('email')}\n disabled={busy}\n style={ghostBtnStyle(busy)}\n >\n back\n </button>\n </FormButtons>\n </>\n )}\n </div>\n );\n}\n\n/* --- btc link form --- */\n\nfunction BtcLinkForm({\n authOrigin,\n didOc,\n onDone,\n onCancel,\n}: {\n authOrigin: string;\n didOc: string | null;\n onDone: () => void;\n onCancel: () => void;\n}): React.ReactElement {\n const [step, setStep] = React.useState<'request' | 'sign'>('request');\n const [addr, setAddr] = React.useState('');\n const [challenge, setChallenge] = React.useState('');\n const [busy, setBusy] = React.useState(false);\n const [err, setErr] = React.useState<string | null>(null);\n // Holds the signature awaiting transfer confirmation when the\n // address is already linked to another account.\n const [pendingSig, setPendingSig] = React.useState<string | null>(null);\n\n async function getChallenge(): Promise<void> {\n if (!didOc) {\n setErr('session not loaded yet · refresh and retry');\n return;\n }\n setBusy(true);\n setErr(null);\n try {\n // /api/challenge is a public primitive — no session needed.\n // audience MUST be the caller's did_oc: the link endpoint\n // verifies expectedAudience=session.did_oc, so a signature\n // obtained here can't be replayed against another account.\n const r = await fetch(\n `${authOrigin}/api/challenge?addr=${encodeURIComponent(addr)}` +\n `&purpose=link&audience=${encodeURIComponent(didOc)}`\n );\n const j = (await r.json()) as { message?: string; reason?: string };\n if (!r.ok || !j.message) throw new Error(j.reason ?? `http_${r.status}`);\n setChallenge(j.message);\n setStep('sign');\n } catch (e) {\n setErr(e instanceof Error ? e.message : 'challenge failed');\n } finally {\n setBusy(false);\n }\n }\n\n async function postLink(sig: string, confirmTransfer: boolean): Promise<void> {\n setBusy(true);\n setErr(null);\n try {\n const r = await fetch(`${authOrigin}/api/auth/link/btc`, {\n method: 'POST',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n message: challenge,\n signature: sig,\n ...(confirmTransfer ? { confirm_transfer: true } : {}),\n }),\n });\n const j = (await r.json()) as { ok?: boolean; reason?: string };\n if (r.status === 409 && j.reason === 'address_linked_elsewhere') {\n setPendingSig(sig);\n setBusy(false);\n return;\n }\n if (handleSudoRequired(j, { purpose: 'link a Bitcoin address' })) return;\n if (isSudoAccountMismatch(j)) {\n setErr(SUDO_ACCOUNT_MISMATCH_MESSAGE);\n setBusy(false);\n return;\n }\n if (!r.ok || !j.ok) throw new Error(j.reason ?? `http_${r.status}`);\n onDone();\n } catch (e) {\n setErr(e instanceof Error ? e.message : 'link failed');\n setBusy(false);\n }\n }\n\n return (\n <div style={panelStyle('accent')} data-oc-li-form=\"btc\">\n <SectionLabel>§ link a Bitcoin address</SectionLabel>\n {step === 'request' ? (\n <>\n <p style={bodyStyle}>\n Enter a Bitcoin address you control. We generate a challenge and your\n wallet signs it (BIP-322).\n </p>\n <input\n type=\"text\"\n value={addr}\n onChange={(e) => setAddr(e.target.value.trim())}\n placeholder=\"bc1q…\"\n autoCapitalize=\"none\"\n spellCheck={false}\n style={{ ...inputStyle, fontFamily: SIGNIN_FONT }}\n />\n {err && <ErrorLine>{err}</ErrorLine>}\n <FormButtons>\n <button\n type=\"button\"\n onClick={() => void getChallenge()}\n disabled={busy || addr.length < 14}\n style={primaryBtnStyle(busy || addr.length < 14)}\n >\n {busy ? 'requesting…' : 'get challenge'}\n </button>\n <button type=\"button\" onClick={onCancel} style={ghostBtnStyle(busy)}>\n cancel\n </button>\n </FormButtons>\n </>\n ) : pendingSig ? (\n <TransferConfirm\n what=\"bitcoin address\"\n busy={busy}\n onConfirm={() => void postLink(pendingSig, true)}\n onCancel={onCancel}\n err={err}\n />\n ) : (\n <>\n <p style={bodyStyle}>\n Sign the challenge with the wallet that controls{' '}\n <span style={{ fontFamily: SIGNIN_FONT, wordBreak: 'break-all' }}>{addr}</span>.\n </p>\n <LazyWalletButton\n address={addr}\n message={challenge}\n showManual\n layout=\"list\"\n heading={null}\n onSigned={(sig: string) => void postLink(sig, false)}\n onError={(e: { message?: string }) =>\n setErr(e?.message ?? 'wallet rejected')\n }\n />\n {err && <ErrorLine>{err}</ErrorLine>}\n <FormButtons>\n <button\n type=\"button\"\n onClick={() => {\n setStep('request');\n setErr(null);\n }}\n disabled={busy}\n style={ghostBtnStyle(busy)}\n >\n back\n </button>\n </FormButtons>\n </>\n )}\n </div>\n );\n}\n\n/* --- transfer-under-dual-proof confirm --- */\n\nfunction TransferConfirm({\n what,\n busy,\n onConfirm,\n onCancel,\n err,\n}: {\n what: string;\n busy: boolean;\n onConfirm: () => void;\n onCancel: () => void;\n err: string | null;\n}): React.ReactElement {\n return (\n <>\n <p style={bodyStyle}>\n This {what} is already linked to another OrangeCheck account. You&apos;ve just\n proven control of it — you can transfer the link to this account under dual proof\n of control. The other account loses this sign-in path; its history stays\n verifiable. Continue?\n </p>\n {err && <ErrorLine>{err}</ErrorLine>}\n <FormButtons>\n <button\n type=\"button\"\n onClick={onConfirm}\n disabled={busy}\n style={dangerBtnStyle(busy)}\n >\n {busy ? 'transferring…' : 'transfer link here'}\n </button>\n <button type=\"button\" onClick={onCancel} disabled={busy} style={ghostBtnStyle(busy)}>\n cancel\n </button>\n </FormButtons>\n </>\n );\n}\n\n/* --- lazy wallet button · wallet-adapter loads on demand, not with the page --- */\n\ninterface WalletButtonProps {\n address: string;\n message: string;\n showManual?: boolean;\n layout?: string;\n heading?: React.ReactNode;\n onSigned: (sig: string) => void;\n onError: (err: { message?: string }, walletId?: string) => void;\n}\n\nexport function LazyWalletButton(props: WalletButtonProps): React.ReactElement {\n const [Comp, setComp] = React.useState<React.ComponentType<WalletButtonProps> | null>(null);\n const [failed, setFailed] = React.useState(false);\n\n React.useEffect(() => {\n let alive = true;\n // A bundler resolves this specifier at build time, so wallet-adapter\n // is a real dependency; only the chunk is deferred.\n import('@orangecheck/wallet-adapter/react')\n .then((m) => {\n if (alive) {\n setComp(\n () => (m as { OcWalletButton: React.ComponentType<WalletButtonProps> })\n .OcWalletButton\n );\n }\n })\n .catch(() => {\n if (alive) setFailed(true);\n });\n return () => {\n alive = false;\n };\n }, []);\n\n if (failed) {\n return (\n <ErrorLine>\n could not load wallet support · refresh to try again\n </ErrorLine>\n );\n }\n if (!Comp) return <span style={hintStyle}>loading wallet options…</span>;\n return <Comp {...props} />;\n}\n\n/* --- helpers --- */\n\nfunction mask(value: string, kind: 'email' | 'btc'): string {\n if (kind === 'email') {\n const [user, host] = value.split('@');\n if (!user || !host) return '••••';\n const m =\n user.length <= 2\n ? '•'.repeat(user.length)\n : user[0] + '•'.repeat(user.length - 2) + user.slice(-1);\n return `${m}@${host}`;\n }\n if (value.length <= 12) return '•'.repeat(value.length);\n return `${value.slice(0, 6)}…${value.slice(-4)}`;\n}\n\n/* --- presentational atoms · also used by bond.tsx; not re-exported from index --- */\n\nexport function SectionLabel({\n children,\n tone = 'primary',\n}: {\n children: React.ReactNode;\n tone?: 'primary' | 'success' | 'warn';\n}): React.ReactElement {\n const color = tone === 'success' ? V.success : tone === 'warn' ? V.warn : V.primary;\n return (\n <div\n style={{\n color,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(10),\n letterSpacing: signinTracking('0.18em'),\n textTransform: SIGNIN_CASE,\n marginBottom: 8,\n }}\n >\n {children}\n </div>\n );\n}\n\nexport function ErrorLine({ children }: { children: React.ReactNode }): React.ReactElement {\n return (\n <p\n role=\"alert\"\n style={{\n margin: '8px 0 0',\n color: V.destructive,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(11),\n }}\n >\n {children}\n </p>\n );\n}\n\nexport function FormButtons({ children }: { children: React.ReactNode }): React.ReactElement {\n return (\n <div style={{ display: 'flex', flexWrap: 'wrap', gap: 8, marginTop: 12 }}>{children}</div>\n );\n}\n\n/* --- style tokens (CSS-variable-themed, same approach as OcSignIn) --- */\n\n\nexport const V = {\n primary: 'var(--primary, #f97316)',\n foreground: 'var(--foreground, #fafafa)',\n muted: 'var(--muted-foreground, #a1a1aa)',\n background: 'var(--background, #0a0a0a)',\n card: 'var(--card, #111113)',\n border: 'var(--border, #27272a)',\n destructive: 'var(--destructive, #ef4444)',\n success: 'var(--success, #22c55e)',\n warn: 'var(--warning, #eab308)',\n} as const;\n\nexport function panelStyle(tone: 'plain' | 'accent' | 'success' | 'warn' | 'default' = 'default'): React.CSSProperties {\n const base: React.CSSProperties = {\n background: V.card,\n border: `1px solid ${V.border}`,\n padding: '1.25rem',\n };\n if (tone === 'plain') return { ...base, padding: 0 };\n if (tone === 'accent') return { ...base, borderColor: V.primary, background: 'var(--card, #111113)' };\n if (tone === 'success') return { ...base, borderColor: V.success };\n if (tone === 'warn') return { ...base, borderColor: V.warn };\n return base;\n}\n\nexport const bodyStyle: React.CSSProperties = {\n color: V.muted,\n fontSize: 13,\n lineHeight: 1.55,\n margin: '0 0 12px',\n};\n\nexport const hintStyle: React.CSSProperties = {\n color: V.muted,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(10.5),\n opacity: 0.8,\n};\n\nconst rowStyle: React.CSSProperties = {\n display: 'flex',\n flexWrap: 'wrap',\n alignItems: 'center',\n justifyContent: 'space-between',\n gap: 12,\n padding: '1rem',\n};\n\nconst rowLabelStyle: React.CSSProperties = {\n color: V.foreground,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(10.5),\n letterSpacing: signinTracking('0.12em'),\n textTransform: SIGNIN_CASE,\n opacity: 0.8,\n};\n\nexport const inputStyle: React.CSSProperties = {\n width: '100%',\n padding: '0.55rem 0.75rem',\n background: V.background,\n color: V.foreground,\n border: `1px solid ${V.border}`,\n borderRadius: signinRadius(6),\n fontFamily: SIGNIN_FONT,\n // 16px — never lower · suppresses iOS Safari focus auto-zoom.\n fontSize: 16,\n boxSizing: 'border-box',\n};\n\nfunction btnBase(disabled: boolean): React.CSSProperties {\n return {\n padding: '0.5rem 0.875rem',\n borderRadius: SIGNIN_BUTTON_RADIUS,\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(10.5),\n letterSpacing: signinTracking('0.12em'),\n textTransform: SIGNIN_CASE,\n cursor: disabled ? 'not-allowed' : 'pointer',\n opacity: disabled ? 0.5 : 1,\n };\n}\n\nexport function primaryBtnStyle(disabled: boolean): React.CSSProperties {\n return {\n ...btnBase(disabled),\n background: V.primary,\n color: 'var(--primary-foreground, #0a0a0a)',\n border: `1px solid ${V.primary}`,\n fontWeight: 600,\n };\n}\n\nexport function ghostBtnStyle(disabled: boolean): React.CSSProperties {\n return {\n ...btnBase(disabled),\n background: V.background,\n color: V.foreground,\n border: `1px solid ${V.border}`,\n };\n}\n\nexport function dangerBtnStyle(disabled: boolean): React.CSSProperties {\n return {\n ...btnBase(disabled),\n background: 'transparent',\n color: V.destructive,\n border: `1px solid ${V.destructive}`,\n };\n}\n\nconst primaryChipStyle: React.CSSProperties = {\n ...btnBase(false),\n background: 'transparent',\n color: V.primary,\n border: `1px solid ${V.primary}`,\n};\n"],"mappings":"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","names":[]}
package/dist/index.js CHANGED
@@ -2144,7 +2144,6 @@ var inputStyle = {
2144
2144
  fontFamily: SIGNIN_FONT,
2145
2145
  // 16px — never lower · suppresses iOS Safari focus auto-zoom.
2146
2146
  fontSize: 16,
2147
- outline: "none",
2148
2147
  boxSizing: "border-box"
2149
2148
  };
2150
2149
  function btnBase(disabled) {
@@ -2251,15 +2250,22 @@ var EMAIL_ERRORS = {
2251
2250
  var SUBMIT_ERRORS = {
2252
2251
  bad_request: "That signature couldn't be read. Copy it again, whole."
2253
2252
  };
2254
- var NETWORK_ERROR = "We couldn't reach the server. Check your connection and try again.";
2253
+ function networkError(authOrigin) {
2254
+ let host = authOrigin;
2255
+ try {
2256
+ host = new URL(authOrigin).host;
2257
+ } catch {
2258
+ }
2259
+ return `We couldn't reach the orangecheck sign-in service at ${host}. Check your connection, then try again.`;
2260
+ }
2255
2261
  function humanSigninError(reason, path, step = "challenge") {
2256
2262
  if (!/^[a-z0-9_]+$/.test(reason)) return reason;
2257
2263
  if (path === "wallet" && step === "submit" && SUBMIT_ERRORS[reason]) return SUBMIT_ERRORS[reason];
2258
2264
  if (path === "email" && EMAIL_ERRORS[reason]) return EMAIL_ERRORS[reason];
2259
2265
  return SIGNIN_ERRORS[reason] ?? (path === "email" ? "That didn't work. Try again, or send a new code." : "That didn't work. Try again.");
2260
2266
  }
2261
- function failureMessage(err, path, step) {
2262
- if (err instanceof TypeError || err instanceof SyntaxError) return NETWORK_ERROR;
2267
+ function failureMessage(err, path, step, authOrigin) {
2268
+ if (err instanceof TypeError || err instanceof SyntaxError) return networkError(authOrigin);
2263
2269
  return humanSigninError(err instanceof Error ? err.message : "failed", path, step);
2264
2270
  }
2265
2271
  var BECH32_ADDRESS = /^(bc1|tb1|bcrt1)[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{8,87}$/i;
@@ -2318,6 +2324,19 @@ function OcSignIn({
2318
2324
  const [path, setPath] = React4__namespace.useState(
2319
2325
  initialPath ?? (providersFirst ? "email" : "wallet")
2320
2326
  );
2327
+ const tabsId = React4__namespace.useId();
2328
+ const tabOrder = providersFirst ? ["email", "wallet"] : ["wallet", "email"];
2329
+ const tabRefs = React4__namespace.useRef({});
2330
+ const onTabKey = (e) => {
2331
+ const i = tabOrder.indexOf(path);
2332
+ const last = tabOrder.length - 1;
2333
+ const to = e.key === "ArrowRight" ? (i + 1) % tabOrder.length : e.key === "ArrowLeft" ? (i + last) % tabOrder.length : e.key === "Home" ? 0 : e.key === "End" ? last : -1;
2334
+ if (to < 0) return;
2335
+ e.preventDefault();
2336
+ const next = tabOrder[to];
2337
+ setPath(next);
2338
+ tabRefs.current[next]?.focus();
2339
+ };
2321
2340
  const [signedIn, setSignedIn] = React4__namespace.useState(null);
2322
2341
  const [linkAlso, setLinkAlso] = React4__namespace.useState(false);
2323
2342
  const [oauthError, setOauthError] = React4__namespace.useState(false);
@@ -2481,7 +2500,7 @@ function OcSignIn({
2481
2500
  "div",
2482
2501
  {
2483
2502
  role: "tablist",
2484
- "aria-label": "signin method",
2503
+ "aria-label": "Sign-in method",
2485
2504
  "data-oc-signin-tabs": "",
2486
2505
  style: {
2487
2506
  display: "flex",
@@ -2489,64 +2508,55 @@ function OcSignIn({
2489
2508
  marginBottom: 16,
2490
2509
  borderBottom: "1px solid var(--border, #27272a)"
2491
2510
  },
2492
- children: providersFirst ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2493
- /* @__PURE__ */ jsxRuntime.jsx(
2494
- SigninTab,
2495
- {
2496
- active: path === "email",
2497
- onClick: () => setPath("email"),
2498
- children: "Email"
2499
- }
2500
- ),
2501
- /* @__PURE__ */ jsxRuntime.jsx(
2502
- SigninTab,
2503
- {
2504
- active: path === "wallet",
2505
- onClick: () => setPath("wallet"),
2506
- children: "Bitcoin wallet"
2507
- }
2508
- )
2509
- ] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2510
- /* @__PURE__ */ jsxRuntime.jsx(
2511
- SigninTab,
2511
+ children: tabOrder.map((p) => /* @__PURE__ */ jsxRuntime.jsx(
2512
+ SigninTab,
2513
+ {
2514
+ id: `${tabsId}-${p}`,
2515
+ controls: `${tabsId}-panel`,
2516
+ active: path === p,
2517
+ onClick: () => setPath(p),
2518
+ onKeyDown: onTabKey,
2519
+ ref: (el) => {
2520
+ tabRefs.current[p] = el;
2521
+ },
2522
+ children: p === "email" ? "Email" : "Bitcoin wallet"
2523
+ },
2524
+ p
2525
+ ))
2526
+ }
2527
+ ),
2528
+ /* @__PURE__ */ jsxRuntime.jsxs(
2529
+ "div",
2530
+ {
2531
+ "data-oc-signin-panel": "",
2532
+ ...bothEnabled ? {
2533
+ id: `${tabsId}-panel`,
2534
+ role: "tabpanel",
2535
+ "aria-labelledby": `${tabsId}-${path}`
2536
+ } : {},
2537
+ children: [
2538
+ showWallet && /* @__PURE__ */ jsxRuntime.jsx(
2539
+ WalletFlow,
2512
2540
  {
2513
- active: path === "wallet",
2514
- onClick: () => setPath("wallet"),
2515
- children: "Bitcoin wallet"
2541
+ authOrigin,
2542
+ audience,
2543
+ add: addMode,
2544
+ credentials,
2545
+ onSuccess: (a, t) => void handleSuccess(a, t, "wallet")
2516
2546
  }
2517
2547
  ),
2518
- /* @__PURE__ */ jsxRuntime.jsx(
2519
- SigninTab,
2548
+ showEmail && /* @__PURE__ */ jsxRuntime.jsx(
2549
+ EmailFlow,
2520
2550
  {
2521
- active: path === "email",
2522
- onClick: () => setPath("email"),
2523
- children: "Email"
2551
+ authOrigin,
2552
+ add: addMode,
2553
+ credentials,
2554
+ onSuccess: (a, t) => void handleSuccess(a, t, "email")
2524
2555
  }
2525
2556
  )
2526
- ] })
2557
+ ]
2527
2558
  }
2528
2559
  ),
2529
- /* @__PURE__ */ jsxRuntime.jsxs("div", { "data-oc-signin-panel": "", role: "tabpanel", children: [
2530
- showWallet && /* @__PURE__ */ jsxRuntime.jsx(
2531
- WalletFlow,
2532
- {
2533
- authOrigin,
2534
- audience,
2535
- add: addMode,
2536
- credentials,
2537
- onSuccess: (a, t) => void handleSuccess(a, t, "wallet")
2538
- }
2539
- ),
2540
- showEmail && /* @__PURE__ */ jsxRuntime.jsx(
2541
- EmailFlow,
2542
- {
2543
- authOrigin,
2544
- add: addMode,
2545
- credentials,
2546
- onSuccess: (a, t) => void handleSuccess(a, t, "email")
2547
- }
2548
- )
2549
- ] }),
2550
2560
  !providersFirst && /* @__PURE__ */ jsxRuntime.jsx(
2551
2561
  ProviderSignIn,
2552
2562
  {
@@ -2578,18 +2588,19 @@ function OcSignIn({
2578
2588
  ] })
2579
2589
  ] });
2580
2590
  }
2581
- function SigninTab({
2582
- active,
2583
- onClick,
2584
- children
2585
- }) {
2591
+ var SigninTab = React4__namespace.forwardRef(function SigninTab2({ id, controls, active, onClick, onKeyDown, children }, ref) {
2586
2592
  return /* @__PURE__ */ jsxRuntime.jsx(
2587
2593
  "button",
2588
2594
  {
2595
+ ref,
2589
2596
  type: "button",
2590
2597
  role: "tab",
2598
+ id,
2591
2599
  "aria-selected": active,
2600
+ "aria-controls": controls,
2601
+ tabIndex: active ? 0 : -1,
2592
2602
  onClick,
2603
+ onKeyDown,
2593
2604
  "data-oc-signin-tab": active ? "active" : "inactive",
2594
2605
  style: {
2595
2606
  minHeight: 44,
@@ -2608,7 +2619,7 @@ function SigninTab({
2608
2619
  children
2609
2620
  }
2610
2621
  );
2611
- }
2622
+ });
2612
2623
  function ProviderIcon({ id }) {
2613
2624
  const common = {
2614
2625
  width: 14,
@@ -2847,7 +2858,7 @@ function WalletFlow({
2847
2858
  step = "submit";
2848
2859
  await submitSignature(challenge.message, challenge.nonce, signature, "bip322");
2849
2860
  } catch (err) {
2850
- setError(failureMessage(err, "wallet", step));
2861
+ setError(failureMessage(err, "wallet", step, authOrigin));
2851
2862
  setSubmitting(false);
2852
2863
  }
2853
2864
  }
@@ -2864,7 +2875,7 @@ function WalletFlow({
2864
2875
  try {
2865
2876
  await submitSignature(manual.message, manual.nonce, sig, void 0);
2866
2877
  } catch (err) {
2867
- setError(failureMessage(err, "wallet", "submit"));
2878
+ setError(failureMessage(err, "wallet", "submit", authOrigin));
2868
2879
  setSubmitting(false);
2869
2880
  }
2870
2881
  }
@@ -3016,7 +3027,7 @@ function EmailFlow({ authOrigin, add, credentials, onSuccess }) {
3016
3027
  setToken(json.token);
3017
3028
  return null;
3018
3029
  } catch {
3019
- return NETWORK_ERROR;
3030
+ return networkError(authOrigin);
3020
3031
  }
3021
3032
  }
3022
3033
  async function start(e) {
@@ -3077,7 +3088,7 @@ function EmailFlow({ authOrigin, add, credentials, onSuccess }) {
3077
3088
  }
3078
3089
  onSuccess(json.account, json.token);
3079
3090
  } catch (err) {
3080
- setCodeError(failureMessage(err, "email", "submit"));
3091
+ setCodeError(failureMessage(err, "email", "submit", authOrigin));
3081
3092
  } finally {
3082
3093
  setSubmitting(false);
3083
3094
  }
@@ -3260,8 +3271,7 @@ var inputStyle2 = {
3260
3271
  // 16px — never below it. A sub-16px font-size makes iOS Safari
3261
3272
  // auto-zoom the viewport on focus; the family rule is >=16px on
3262
3273
  // every form field. Desktop reads fine at 16 too.
3263
- fontSize: 16,
3264
- outline: "none"
3274
+ fontSize: 16
3265
3275
  };
3266
3276
  var linkAlsoStyle = {
3267
3277
  display: "flex",