@orangecheck/auth-client 2.36.0 → 2.37.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
@@ -1,3 +1,8 @@
1
+ import {
2
+ OcSignIn,
3
+ familyReturnTarget,
4
+ safeReturnTo
5
+ } from "./chunk-VOO5KQQW.mjs";
1
6
  import {
2
7
  useStepUpAuth,
3
8
  useWebAuthnList,
@@ -7,7 +12,7 @@ import {
7
12
  OcBond,
8
13
  bondFloorsText,
9
14
  fetchOcBond
10
- } from "./chunk-NCJZBLNF.mjs";
15
+ } from "./chunk-EFMTFRCS.mjs";
11
16
  import {
12
17
  OcAccountChip,
13
18
  OcAccountPill,
@@ -15,27 +20,23 @@ import {
15
20
  OcSignInButton,
16
21
  useOcAddressSuggestion
17
22
  } from "./chunk-EZKAMTG7.mjs";
18
- import {
19
- OcSessionProvider,
20
- useOcSession,
21
- useOptionalOcSession
22
- } from "./chunk-E2XQ7YED.mjs";
23
- import {
24
- OcSignIn,
25
- familyReturnTarget,
26
- safeReturnTo
27
- } from "./chunk-FZ6RPWWR.mjs";
28
23
  import {
29
24
  OcLinkedIdentities,
30
25
  fetchOcLinkedIdentities
31
- } from "./chunk-HOCQ722N.mjs";
26
+ } from "./chunk-BMMNHG74.mjs";
32
27
  import {
33
28
  SUDO_ACCOUNT_MISMATCH_MESSAGE,
34
29
  handleSudoRequired,
35
30
  isSudoAccountMismatch,
36
31
  redirectToSudo
37
32
  } from "./chunk-XCZVSE7M.mjs";
33
+ import "./chunk-U7IUGDLW.mjs";
38
34
  import "./chunk-H2TXOCXH.mjs";
35
+ import {
36
+ OcSessionProvider,
37
+ useOcSession,
38
+ useOptionalOcSession
39
+ } from "./chunk-E2XQ7YED.mjs";
39
40
  import {
40
41
  DEFAULT_CONFIG,
41
42
  DISPLAY_IDENTITY_KINDS,
@@ -15,8 +15,9 @@ import {
15
15
  inputStyle,
16
16
  panelStyle,
17
17
  primaryBtnStyle
18
- } from "./chunk-HOCQ722N.mjs";
18
+ } from "./chunk-BMMNHG74.mjs";
19
19
  import "./chunk-XCZVSE7M.mjs";
20
+ import "./chunk-U7IUGDLW.mjs";
20
21
  import "./chunk-H2TXOCXH.mjs";
21
22
  import "./chunk-VRLFZAGK.mjs";
22
23
  import "./chunk-CYR7J4UY.mjs";
package/dist/signin.mjs CHANGED
@@ -5,9 +5,10 @@ import {
5
5
  looksLikeBitcoinAddress,
6
6
  networkError,
7
7
  safeReturnTo
8
- } from "./chunk-FZ6RPWWR.mjs";
9
- import "./chunk-HOCQ722N.mjs";
8
+ } from "./chunk-VOO5KQQW.mjs";
9
+ import "./chunk-BMMNHG74.mjs";
10
10
  import "./chunk-XCZVSE7M.mjs";
11
+ import "./chunk-U7IUGDLW.mjs";
11
12
  import "./chunk-H2TXOCXH.mjs";
12
13
  import "./chunk-VRLFZAGK.mjs";
13
14
  import "./chunk-CYR7J4UY.mjs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@orangecheck/auth-client",
3
- "version": "2.36.0",
3
+ "version": "2.37.0",
4
4
  "description": "React hooks and components for the cross-subdomain oc_session. Drop-in sign-in button, account chip with dashboard link, session-aware address input, and useOcSession() hook.",
5
5
  "keywords": [
6
6
  "orangecheck",
@@ -46,6 +46,7 @@
46
46
  "prepublishOnly": "npm run clean && npm run build"
47
47
  },
48
48
  "dependencies": {
49
+ "@orangecheck/bip322": "^1.1.2",
49
50
  "@orangecheck/wallet-adapter": "^0.7.0",
50
51
  "@simplewebauthn/browser": "^13.3.0"
51
52
  },
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/signin.tsx"],"sourcesContent":["/**\n * @orangecheck/auth-client · OcSignIn\n *\n * In-place dual-path signin component for every `X.ochk.io` consumer site.\n * Calls the cross-subdomain auth host (`ochk.io`) via family-CORS so the\n * `.ochk.io` session cookie lands without any redirect.\n *\n * Two paths, one component:\n *\n * 1. Bitcoin wallet · BIP-322\n * - Paste address, in-page wallet extension signs a challenge\n * - POSTs { message, signature, expectedNonce, expectedAudience,\n * expectedPurpose } to ochk.io/api/auth/signin\n * - Identity is `did:oc:<rand>` linked to the BTC address\n *\n * 2. Email · OTP\n * - Enter email → ochk.io/api/auth/email-otp/start delivers a code\n * - Enter code → ochk.io/api/auth/email-otp/verify lands the cookie\n * - Identity is `did:oc:<rand>`. No wallet is created here: a Fedimint\n * wallet is provisioned client-side on first /me visit, and its sats\n * are held by the FEDERATION'S GUARDIANS — not by OrangeCheck, which\n * coordinates and never custodies (see oc-www's email-otp/verify\n * route). Naming the custodian is family invariant 8; the old wording\n * here and in the form copy said \"a custodial wallet\" with no subject,\n * which reads as OC holding it and contradicts the charter.\n *\n * **Both paths resolve to the same did:oc canonical identity** via the\n * auth host's account-linking logic — a user who started with email can\n * later link their BTC address (and vice versa). One identity, two doors,\n * persistent across every family subdomain.\n *\n * On success, the component **hard-navigates** (window.location.assign)\n * rather than calling onSuccess + relying on the consumer to route. This\n * is non-negotiable: OcSessionProvider does not auto-refetch /api/auth/me\n * on client-side route changes, so a soft redirect leaves it with stale\n * `status: 'anonymous'`. Hard nav forces the provider to remount with\n * the fresh cookie. Override via onSuccess if you must.\n */\n\nimport * as React from 'react';\n\nimport { LinkPromptStep } from './linked-identities';\nimport { stripFragment } from './return-to';\nimport { clearTabSession } from './tab-session';\nimport type { OcAccount } from './types';\nimport {\n SIGNIN_ARROW,\n SIGNIN_BUTTON_RADIUS,\n SIGNIN_CASE,\n SIGNIN_FONT,\n SIGNIN_STRONG,\n signinRadius,\n signinRoleSize,\n signinSize,\n signinTracking,\n} from './appearance';\n\n/* --- props --- */\n\ninterface OcSignInBaseProps {\n /**\n * Consumer-site audience URL passed as `expectedAudience` on the\n * BIP-322 challenge. e.g. `'https://vault.ochk.io'`. Required —\n * the challenge nonce is bound to this audience.\n */\n audience: string;\n /**\n * Where to navigate after success. Defaults to `'/'`.\n * Open-redirect-safe: a same-origin path (must start with `/`, NOT\n * `//`) or an absolute `https://` URL on `ochk.io` / `*.ochk.io` —\n * the add-another-account flow starts on a consumer subdomain and\n * must round-trip back to it, including through the OAuth provider\n * hop. Anything else is ignored. When omitted, the component reads\n * `?return_to=` (or `?next=`) from the page URL — so the auth\n * host's `/signin?add=1&return_to=…` entry point Just Works, the\n * same way the `add` prop auto-detects `?add=1`.\n */\n returnTo?: string;\n /**\n * Override the default hard-navigation behavior. When provided,\n * called with the account and the session JWT. The component will\n * NOT navigate — the caller is responsible. Use for custom\n * post-signin routing — e.g. /popup/signin postMessages\n * `{ account, token }` to its opener so a cross-domain integrator\n * (different eTLD+1 from .ochk.io, which the HttpOnly cookie can't\n * reach) can verify the session via JWKS.\n */\n onSuccess?: (account: OcAccount, token?: string) => void;\n /**\n * Async post-success routing. When provided (and `onSuccess` is\n * not), the component awaits `resolveReturnTo(account)` and\n * hard-navigates to the result instead of the static `returnTo`.\n *\n * This is the seam that lets a site keep persona-aware routing\n * without forking the ceremony — e.g. me.ochk.io resolves\n * `/api/me/intent` and routes to `/me/developer` | `/me/operator`\n * | `/me`. The returned value is open-redirect-checked exactly\n * like `returnTo` (same-origin paths only); on a resolver throw\n * the component falls back to the static `returnTo`.\n */\n resolveReturnTo?: (account: OcAccount) => string | Promise<string>;\n /**\n * Override the auth host. Defaults to `'https://ochk.io'`. For\n * preview / dev / staging only.\n */\n authOrigin?: string;\n /**\n * Initial visible tab. Defaults to `'email'` (since {@link providersFirst}\n * is on by default); resolves to `'wallet'` only when\n * `providersFirst={false}` and this is left unspecified.\n */\n initialPath?: 'wallet' | 'email';\n /**\n * Re-order the ceremony so the third-party providers (Google / GitHub)\n * render **above** the wallet + email panel, and the email path is the\n * default active tab. **On by default — the family standard.** A\n * first-time, non-Bitcoiner visitor sees the most familiar on-ramp\n * (Google / GitHub / email one-time code) first; the BIP-322 wallet path\n * stays one tab away, reframed as the most-sovereign option.\n *\n * Pass `providersFirst={false}` for the legacy bitcoin-first ordering.\n * Honors family rule #3 — email / OAuth is the easy bridge, the Bitcoin\n * address remains the canonical identity it resolves to.\n */\n providersFirst?: boolean;\n /**\n * Disable one of the two paths. Default is both enabled.\n * Useful for B2B-only sites that don't want to expose email-OTP.\n */\n paths?: { wallet?: boolean; email?: boolean };\n /**\n * Whether to show the \"also link my other identity\" checkbox on the\n * sign-in form. **On by default.** The checkbox is optional and\n * unchecked by default; it is shown on both the wallet and email\n * paths. If the user ticks it, the complementary identity's link\n * ceremony (BIP-322 for a wallet, OTP for an email) runs inline\n * immediately after a successful sign-in — before `onSuccess` /\n * `returnTo`, so it composes with custom routing — because the\n * sign-in just proved one credential and the link ceremony proves\n * the second. Pass `linkPrompt={false}` to omit the checkbox.\n */\n linkPrompt?: boolean;\n /**\n * Multi-account · when `true`, the sign-in is performed in \"add\"\n * mode: the resulting session is appended to the browser's existing\n * roster instead of replacing it. The previously-active account\n * stays signed in and remains a switch target on the auth host. Off\n * by default (back-compat). The component also reads `?add=1` from\n * the URL search params and treats it the same — so the auth host's\n * `/signin?add=1` entry point Just Works without any extra prop.\n *\n * The `add` flag is forwarded as a body field to the host's\n * `/api/auth/signin` and `/api/auth/email-otp/verify`; if the host\n * hasn't deployed the multi-account migration yet it silently\n * ignores the field and the call falls back to ordinary signin.\n */\n add?: boolean;\n /** className for the outer container. */\n className?: string;\n}\n\n/**\n * Where the ceremony's session goes.\n *\n * `family` (default): a `*.ochk.io` site. The auth host sets the shared\n * `.ochk.io` cookie and the component navigates.\n *\n * `first-party`: orangecheck's own site on another domain (ochk.app). Every\n * call to the auth host omits credentials, so the family cookie is neither\n * sent nor set; the host answers with a token bound to `audience`, handed to\n * `onSuccess` for the site to turn into its own cookie. No add-account or\n * link prompts (there is no family session to add to), and provider buttons\n * appear only when `providerHref` says where the site starts each one.\n */\nexport type OcSignInProps = OcSignInBaseProps &\n (\n | { mode?: 'family'; providerHref?: undefined }\n | {\n mode: 'first-party';\n onSuccess: (account: OcAccount, token?: string) => void;\n /** The site's own start URL for a provider (`google`, `github`). */\n providerHref?: (providerId: string) => string;\n }\n );\n\n/* --- shared types --- */\n\ninterface SigninJsonOk {\n ok: true;\n account: OcAccount;\n /**\n * The session JWT, echoed alongside the `Set-Cookie`. Cross-domain\n * integrators capture this to verify the session via JWKS — the\n * HttpOnly `.ochk.io` cookie never reaches their origin. Family\n * `.ochk.io` sites ignore it and rely on the cookie.\n */\n token?: string;\n}\n\ninterface SigninJsonErr {\n ok?: false;\n reason?: string;\n error?: string;\n issues?: unknown;\n}\n\ntype SigninJson = SigninJsonOk | SigninJsonErr;\n\n/* --- helpers --- */\n\n/**\n * A relative path is same-origin only if it RESOLVES to this origin.\n *\n * Prefix checks are not enough. `startsWith('/') && !startsWith('//')` accepts\n * `/\\evil.example`, and the WHATWG URL parser treats a backslash as a slash in\n * special schemes, so the browser resolves it to `https://evil.example/`:\n *\n * new URL('/\\evil.example', 'https://attest.ochk.io').href\n * -> 'https://evil.example/'\n *\n * Since hardNavigate() below hands its argument to window.location.assign,\n * that was a post-authentication open redirect on every consumer of this\n * package — the user completes a real sign-in on a real ochk.io domain and\n * lands on an attacker's page. `\\t`, `\\n` and `\\r` are stripped by the parser\n * too, so `/\\t/evil.example` is the same trick with different bytes.\n *\n * So instead of blacklisting characters, resolve and compare: whatever the URL\n * parser makes of the input is what the browser will navigate to, and that is\n * the thing that has to be same-origin.\n */\nfunction isSameOriginPath(candidate: string, origin: string): boolean {\n try {\n return new URL(candidate, origin).origin === origin;\n } catch {\n return false;\n }\n}\n\n/** Origin to resolve relative candidates against. Any absolute https origin\n * works for the comparison; on the server there is no window to read. */\nfunction resolutionOrigin(): string {\n return typeof window === 'undefined' ? 'https://ochk.io' : window.location.origin;\n}\n\nexport function safeReturnTo(input: string | undefined): string {\n const candidate = input ?? '/';\n if (typeof candidate !== 'string') return '/';\n if (!candidate.startsWith('/')) return '/';\n return isSameOriginPath(candidate, resolutionOrigin()) ? candidate : '/';\n}\n\n/**\n * Family-aware return target. Accepts a same-origin relative path\n * (exactly like {@link safeReturnTo}) OR an absolute `https://` URL on\n * `ochk.io` / `*.ochk.io` — mirroring the auth host's own post-signin\n * redirect allowlist. Returns `undefined` when the input is neither, so\n * callers can layer fallbacks.\n */\nexport function familyReturnTarget(input: string | undefined | null): string | undefined {\n if (typeof input !== 'string' || input.length === 0) return undefined;\n // Same-origin relative path — resolved, not prefix-matched. See\n // isSameOriginPath for why `/\\evil.example` defeated the old check.\n if (input.startsWith('/') && isSameOriginPath(input, resolutionOrigin())) {\n return input;\n }\n try {\n const u = new URL(input);\n if (u.protocol !== 'https:') return undefined;\n const host = u.hostname.toLowerCase();\n if (host === 'ochk.io' || host.endsWith('.ochk.io')) return u.toString();\n } catch {\n // not an absolute URL either\n }\n return undefined;\n}\n\nfunction hardNavigate(target: string): void {\n if (typeof window === 'undefined') return;\n window.location.assign(target);\n}\n\n/* --- failures people can act on --- */\n\n/**\n * The auth host answers with machine reasons. Its /api/auth/signin says it\n * exposes them \"so the /signin UI can give an actionable message\", and this\n * form printed them verbatim instead: typing \"hello\" as an address showed\n * `bad_request`. Each reason maps to what happened and what to do next.\n */\nconst SIGNIN_ERRORS: Record<string, string> = {\n bad_request: \"That doesn't look like a Bitcoin address.\",\n invalid_challenge:\n \"That signature doesn't match this address. Sign with the wallet that holds it.\",\n sig_invalid: \"That signature doesn't match this address. Sign with the wallet that holds it.\",\n sig_unsupported_scheme:\n \"We can't check this wallet's signature format yet. Try another wallet, or use email.\",\n expired: 'That request timed out. Try again for a fresh one.',\n not_yet_valid: 'That request is not valid yet. Check your device clock, then try again.',\n nonce_mismatch: \"That request didn't match. Try again for a fresh one.\",\n audience_mismatch: \"That request didn't match this site. Reload the page and try again.\",\n purpose_mismatch: \"That request didn't match. Try again for a fresh one.\",\n malformed: \"That signature couldn't be read. Copy it again, whole.\",\n cross_site_blocked: 'Your browser blocked this request. Reload the page and try again.',\n rate_limited: 'Too many tries. Wait a minute, then try again.',\n invalid_email: \"That email address doesn't look right.\",\n mailer_unavailable: \"We couldn't send the email right now. Try again in a minute.\",\n mailer_error: \"We couldn't send the email right now. Try again in a minute.\",\n wrong_code: \"That code isn't right. Check the newest email from us.\",\n replay: 'That code was already used. Send a new one.',\n invalid_token: 'That code is no longer valid. Send a new one.',\n email_mismatch: 'That code was sent to a different address. Send a new one.',\n};\n\nconst EMAIL_ERRORS: Record<string, string> = {\n expired: 'That code expired. Send a new one.',\n};\n\n/**\n * `bad_request` means the host's body schema failed. Fetching a challenge\n * that is the address; submitting a signature it is the pasted signature\n * (under 10 characters, say), which is not an address problem at all.\n */\nconst SUBMIT_ERRORS: Record<string, string> = {\n bad_request: \"That signature couldn't be read. Copy it again, whole.\",\n};\n\n/** The auth host by name, so an outage reads as theirs and not the page's. */\nexport function networkError(authOrigin: string): string {\n let host = authOrigin;\n try {\n host = new URL(authOrigin).host;\n } catch {\n // not a URL: name it as given\n }\n return `We couldn't reach the orangecheck sign-in service at ${host}. Check your connection, then try again.`;\n}\n\nexport function humanSigninError(\n reason: string,\n path: 'wallet' | 'email',\n step: 'challenge' | 'submit' = 'challenge'\n): string {\n // A reason that is not a code is already a sentence.\n if (!/^[a-z0-9_]+$/.test(reason)) return reason;\n if (path === 'wallet' && step === 'submit' && SUBMIT_ERRORS[reason]) return SUBMIT_ERRORS[reason];\n if (path === 'email' && EMAIL_ERRORS[reason]) return EMAIL_ERRORS[reason];\n return (\n SIGNIN_ERRORS[reason] ??\n (path === 'email'\n ? \"That didn't work. Try again, or send a new code.\"\n : \"That didn't work. Try again.\")\n );\n}\n\n/**\n * A failed fetch (\"Failed to fetch\") or an HTML error page parsed as JSON\n * (a SyntaxError) is not prose meant for a person; everything else thrown\n * here carries a host reason code. A wallet's failure never comes here.\n */\nfunction failureMessage(\n err: unknown,\n path: 'wallet' | 'email',\n step: 'challenge' | 'submit',\n authOrigin: string\n): string {\n if (err instanceof TypeError || err instanceof SyntaxError) return networkError(authOrigin);\n return humanSigninError(err instanceof Error ? err.message : 'failed', path, step);\n}\n\nconst BECH32_ADDRESS = /^(bc1|tb1|bcrt1)[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{8,87}$/i;\nconst BASE58_ADDRESS = /^[123mn][1-9A-HJ-NP-Za-km-z]{25,34}$/;\n\n/**\n * Shape check only, so a typo fails here with a clear message instead of\n * fetching a challenge for it and then blaming a missing wallet extension.\n * The host still verifies the signature against the real address.\n */\nexport function looksLikeBitcoinAddress(input: string): boolean {\n const s = input.trim();\n return BECH32_ADDRESS.test(s) || BASE58_ADDRESS.test(s);\n}\n\n/* --- main component --- */\n\nexport function OcSignIn({\n audience,\n returnTo,\n onSuccess,\n resolveReturnTo,\n linkPrompt: linkPromptProp = true,\n add: addProp,\n authOrigin = 'https://ochk.io',\n initialPath,\n providersFirst = true,\n paths,\n className,\n mode,\n providerHref,\n}: OcSignInProps): React.ReactElement {\n const walletEnabled = paths?.wallet ?? true;\n const emailEnabled = paths?.email ?? true;\n const firstParty = mode === 'first-party';\n // First-party: the family cookie must be neither sent nor set.\n const credentials: RequestCredentials = firstParty ? 'omit' : 'include';\n const linkPrompt = linkPromptProp && !firstParty;\n\n // Return target · prop wins; otherwise honor `?return_to=` / `?next=`\n // from the URL — the auth host's `/signin?add=1&return_to=…` entry\n // point needs the OAuth provider hop to carry the same target the\n // embedding page honors (the add-another-account fix: a vault user\n // adding an account via Google must land back on vault, not on the\n // host's homepage). Absolute https://*.ochk.io URLs are allowed;\n // everything else clamps to `/`.\n const [resolvedReturn, setResolvedReturn] = React.useState<string>(\n () => familyReturnTarget(returnTo) ?? safeReturnTo(returnTo)\n );\n React.useEffect(() => {\n const fromProp = familyReturnTarget(returnTo);\n if (fromProp) {\n setResolvedReturn(fromProp);\n return;\n }\n if (typeof window === 'undefined') return;\n const q = new URLSearchParams(window.location.search);\n const fromQuery =\n familyReturnTarget(q.get('return_to')) ?? familyReturnTarget(q.get('next'));\n setResolvedReturn(fromQuery ?? '/');\n }, [returnTo]);\n\n // Multi-account · prop wins; otherwise honor `?add=1` from the URL\n // so the auth host's /signin?add=1 entry point doesn't need any\n // extra wiring on every consumer's sign-in page.\n const [addMode, setAddMode] = React.useState<boolean>(Boolean(addProp) && !firstParty);\n React.useEffect(() => {\n if (typeof window === 'undefined') return;\n if (firstParty) {\n setAddMode(false);\n return;\n }\n if (addProp !== undefined) {\n setAddMode(Boolean(addProp));\n return;\n }\n setAddMode(new URLSearchParams(window.location.search).get('add') === '1');\n }, [addProp, firstParty]);\n\n // When providers lead the ceremony (ochk.io homepage), the email path\n // is the friendlier default tab; otherwise the canonical bitcoin-first\n // default holds. An explicit `initialPath` always wins.\n const [path, setPath] = React.useState<'wallet' | 'email'>(\n initialPath ?? (providersFirst ? 'email' : 'wallet')\n );\n // The two methods are one tablist: one Tab stop, arrows and Home/End move\n // between them, and moving selects (both are always offered).\n const tabsId = React.useId();\n const tabOrder: Array<'wallet' | 'email'> = providersFirst\n ? ['email', 'wallet']\n : ['wallet', 'email'];\n const tabRefs = React.useRef<Partial<Record<'wallet' | 'email', HTMLButtonElement | null>>>({});\n const onTabKey = (e: React.KeyboardEvent<HTMLButtonElement>) => {\n const i = tabOrder.indexOf(path);\n const last = tabOrder.length - 1;\n const to =\n e.key === 'ArrowRight' ? (i + 1) % tabOrder.length\n : e.key === 'ArrowLeft' ? (i + last) % tabOrder.length\n : e.key === 'Home' ? 0\n : e.key === 'End' ? last\n : -1;\n if (to < 0) return;\n e.preventDefault();\n const next = tabOrder[to]!;\n setPath(next);\n tabRefs.current[next]?.focus();\n };\n // Set once sign-in succeeds and the account is missing its\n // complementary identity — the component then renders the focused\n // link step (LinkPromptStep) before running `proceed`.\n const [signedIn, setSignedIn] = React.useState<{\n method: 'btc' | 'email';\n didOc: string;\n proceed: () => void;\n } | null>(null);\n // The optional \"also link my other identity\" checkbox on the form.\n const [linkAlso, setLinkAlso] = React.useState(false);\n // Set when the user is bounced back here after a failed provider\n // (e.g. Google) sign-in — the auth host redirects to\n // `/signin?oauth_error=…`.\n const [oauthError, setOauthError] = React.useState(false);\n React.useEffect(() => {\n if (typeof window === 'undefined') return;\n if (new URLSearchParams(window.location.search).has('oauth_error')) {\n setOauthError(true);\n }\n }, []);\n\n const navigate = React.useCallback(\n async (account: OcAccount) => {\n if (resolveReturnTo) {\n try {\n const resolved = familyReturnTarget(await resolveReturnTo(account));\n hardNavigate(resolved ?? resolvedReturn);\n return;\n } catch {\n // resolver failed — fall through to the static returnTo\n }\n }\n hardNavigate(resolvedReturn);\n },\n [resolveReturnTo, resolvedReturn]\n );\n\n const handleSuccess = React.useCallback(\n async (account: OcAccount, token: string | undefined, via: 'wallet' | 'email') => {\n // The post-sign-in handoff — custom `onSuccess`, else navigation.\n const proceed = () => {\n // Per-tab · the user just completed a ceremony IN this tab —\n // it must adopt the new identity, not keep a stale pin.\n clearTabSession();\n if (onSuccess) onSuccess(account, token);\n else void navigate(account);\n };\n\n // Linking is opt-in via the form checkbox. Unchecked (or the\n // checkbox suppressed) → ordinary sign-in.\n if (!linkPrompt || !linkAlso) {\n proceed();\n return;\n }\n\n // Checked: run the complementary identity's link ceremony now.\n // /api/auth/me resolves the did:oc the BIP-322 link challenge is\n // bound to; a failure here must not strand the signed-in user.\n try {\n const meRes = await fetch(`${authOrigin}/api/auth/me`, {\n credentials: 'include',\n headers: { Accept: 'application/json' },\n });\n const me = meRes.ok\n ? ((await meRes.json()) as { account?: { did_oc?: string } })\n : null;\n const didOc = me?.account?.did_oc;\n if (didOc) {\n setSignedIn({\n method: via === 'email' ? 'btc' : 'email',\n didOc,\n proceed,\n });\n return;\n }\n } catch {\n // /api/auth/me unreachable — proceed without the link step.\n }\n proceed();\n },\n [onSuccess, linkPrompt, linkAlso, navigate, authOrigin]\n );\n\n if (!walletEnabled && !emailEnabled) {\n return (\n <div\n className={className}\n data-oc-signin=\"\"\n style={{\n padding: '1rem',\n border: '1px solid var(--border, #27272a)',\n color: 'var(--foreground, #fafafa)',\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(12),\n }}\n >\n no signin paths enabled — set <code>paths.wallet</code> or{' '}\n <code>paths.email</code> to <code>true</code>\n </div>\n );\n }\n\n if (signedIn) {\n return (\n <div className={className} data-oc-signin=\"\">\n <LinkPromptStep\n method={signedIn.method}\n didOc={signedIn.didOc}\n authOrigin={authOrigin}\n onResolved={signedIn.proceed}\n />\n </div>\n );\n }\n\n const showWallet = walletEnabled && (path === 'wallet' || !emailEnabled);\n const showEmail = emailEnabled && (path === 'email' || !walletEnabled);\n const bothEnabled = walletEnabled && emailEnabled;\n // The method the user will actually sign in with — the active tab,\n // clamped when only one path is enabled. Drives the checkbox wording.\n const activeMethod: 'wallet' | 'email' = !emailEnabled\n ? 'wallet'\n : !walletEnabled\n ? 'email'\n : path;\n\n return (\n <div className={className} data-oc-signin=\"\">\n {oauthError && (\n <div\n data-oc-signin-oauth-error=\"\"\n style={{\n marginBottom: 14,\n padding: '0.6rem 0.75rem',\n border: '1px solid var(--destructive, #ef4444)',\n borderRadius: signinRadius(6),\n color: 'var(--destructive, #ef4444)',\n fontFamily: SIGNIN_FONT,\n fontSize: signinRoleSize('text', signinSize(11)),\n }}\n >\n That sign-in didn&apos;t finish. Try again, or use another way below.\n </div>\n )}\n {addMode && (\n <div\n data-oc-signin-add-mode=\"\"\n style={{\n marginBottom: 14,\n padding: '0.6rem 0.75rem',\n border: '1px solid var(--primary, #f97316)',\n borderLeftWidth: 3,\n borderRadius: signinRadius(4),\n background: 'color-mix(in srgb, var(--primary, #f97316) 8%, transparent)',\n color: 'var(--foreground, #fafafa)',\n fontFamily: SIGNIN_FONT,\n fontSize: signinRoleSize('text', signinSize(11)),\n lineHeight: 1.6,\n }}\n >\n <strong\n style={{\n color: 'var(--primary, #f97316)',\n letterSpacing: signinTracking('0.12em'),\n textTransform: SIGNIN_CASE,\n fontSize: signinSize(10),\n display: 'block',\n marginBottom: 3,\n }}\n >\n § adding another account\n </strong>\n Your current OrangeCheck account stays signed in. The new account joins your\n browser&apos;s roster; you can switch between them anytime from the account\n menu.\n </div>\n )}\n {/* Providers-first (ochk.io homepage): the familiar Google /\n GitHub on-ramp leads, with the wallet/email panel below the\n divider. Everywhere else this block stays at the bottom. */}\n {providersFirst && (\n <ProviderSignIn\n authOrigin={authOrigin}\n returnTo={resolvedReturn}\n add={addMode}\n credentials={credentials}\n firstParty={firstParty}\n providerHref={providerHref}\n first\n />\n )}\n {bothEnabled && (\n <div\n role=\"tablist\"\n aria-label=\"Sign-in method\"\n data-oc-signin-tabs=\"\"\n style={{\n display: 'flex',\n gap: 8,\n marginBottom: 16,\n borderBottom: '1px solid var(--border, #27272a)',\n }}\n >\n {/* When providers lead, the easy email path reads first\n and the wallet path sits beside it as the upgrade. */}\n {tabOrder.map((p) => (\n <SigninTab\n key={p}\n id={`${tabsId}-${p}`}\n controls={`${tabsId}-panel`}\n active={path === p}\n onClick={() => setPath(p)}\n onKeyDown={onTabKey}\n ref={(el) => {\n tabRefs.current[p] = el;\n }}\n >\n {p === 'email' ? 'Email' : 'Bitcoin wallet'}\n </SigninTab>\n ))}\n </div>\n )}\n\n <div\n data-oc-signin-panel=\"\"\n {...(bothEnabled\n ? {\n id: `${tabsId}-panel`,\n role: 'tabpanel',\n 'aria-labelledby': `${tabsId}-${path}`,\n }\n : {})}\n >\n {showWallet && (\n <WalletFlow\n authOrigin={authOrigin}\n audience={audience}\n add={addMode}\n credentials={credentials}\n onSuccess={(a, t) => void handleSuccess(a, t, 'wallet')}\n />\n )}\n {showEmail && (\n <EmailFlow\n authOrigin={authOrigin}\n add={addMode}\n credentials={credentials}\n onSuccess={(a, t) => void handleSuccess(a, t, 'email')}\n />\n )}\n </div>\n\n {!providersFirst && (\n <ProviderSignIn\n authOrigin={authOrigin}\n returnTo={resolvedReturn}\n add={addMode}\n credentials={credentials}\n firstParty={firstParty}\n providerHref={providerHref}\n />\n )}\n\n {linkPrompt && (\n <label data-oc-signin-linkalso=\"\" style={linkAlsoStyle}>\n <input\n type=\"checkbox\"\n checked={linkAlso}\n onChange={(e) => setLinkAlso(e.target.checked)}\n style={{ margin: 0, accentColor: 'var(--primary, #f97316)' }}\n />\n <span>\n After signing in, also link{' '}\n {activeMethod === 'email' ? 'a Bitcoin wallet' : 'an email'}{' '}\n <span>— optional, one more signature.</span>\n </span>\n </label>\n )}\n </div>\n );\n}\n\n/* --- tab --- */\n\nconst SigninTab = React.forwardRef<\n HTMLButtonElement,\n {\n id: string;\n controls: string;\n active: boolean;\n onClick: () => void;\n onKeyDown: (e: React.KeyboardEvent<HTMLButtonElement>) => void;\n children: React.ReactNode;\n }\n>(function SigninTab({ id, controls, active, onClick, onKeyDown, children }, ref) {\n return (\n <button\n ref={ref}\n type=\"button\"\n role=\"tab\"\n id={id}\n aria-selected={active}\n aria-controls={controls}\n tabIndex={active ? 0 : -1}\n onClick={onClick}\n onKeyDown={onKeyDown}\n data-oc-signin-tab={active ? 'active' : 'inactive'}\n style={{\n minHeight: 44,\n padding: '0.6rem 0.875rem',\n background: 'transparent',\n border: 'none',\n borderBottom: active\n ? '2px solid var(--primary, #f97316)'\n : '2px solid transparent',\n color: active\n ? 'var(--foreground, #fafafa)'\n : 'var(--muted-foreground, #a1a1aa)',\n fontFamily: SIGNIN_FONT,\n fontSize: signinRoleSize('label', signinSize(11)),\n letterSpacing: signinTracking('0.12em'),\n textTransform: SIGNIN_CASE,\n cursor: 'pointer',\n marginBottom: -1,\n }}\n >\n {children}\n </button>\n );\n});\n\n/* --- third-party provider sign-in --- */\n\ninterface OAuthProviderEntry {\n id: string;\n label: string;\n}\n\n/**\n * Brand glyph for a known OAuth provider — rendered inside the\n * provider button so each entry is visually recognisable, but drawn\n * in `currentColor` so it picks up the OC muted-foreground theme\n * (the family palette wins over each vendor's brand colours by\n * design). Unknown providers fall through to no icon — the button\n * still renders, just text-only, so the registry is forward-compatible.\n */\nfunction ProviderIcon({ id }: { id: string }): React.ReactElement | null {\n const common = {\n width: 14,\n height: 14,\n viewBox: '0 0 24 24',\n fill: 'currentColor',\n 'aria-hidden': true,\n style: { flex: '0 0 auto' } as React.CSSProperties,\n } as const;\n if (id === 'google') {\n return (\n <svg {...common}>\n <path d=\"M12.48 10.92v3.28h7.84c-.24 1.84-.853 3.187-1.787 4.133-1.147 1.147-2.933 2.4-6.053 2.4-4.827 0-8.6-3.893-8.6-8.72s3.773-8.72 8.6-8.72c2.6 0 4.507 1.027 5.907 2.347l2.307-2.307C18.747 1.44 16.133 0 12.48 0 5.867 0 .307 5.387.307 12s5.56 12 12.173 12c3.573 0 6.267-1.173 8.373-3.36 2.16-2.16 2.84-5.213 2.84-7.667 0-.76-.053-1.467-.173-2.053H12.48z\" />\n </svg>\n );\n }\n if (id === 'github') {\n return (\n <svg {...common}>\n <path d=\"M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\" />\n </svg>\n );\n }\n return null;\n}\n\n/**\n * The secondary sign-in block — rendered below BIP-322 and email-OTP,\n * visibly less prominent. Each button is a plain navigation to the\n * auth host, which runs the OAuth dance and mints the family\n * `.ochk.io` session.\n *\n * The provider list is fetched from the auth host's\n * `/api/auth/providers` — a button appears only once that provider's\n * credentials are configured host-side. So enabling GitHub / Apple\n * later is a host env change, not a redeploy of every consumer site.\n * With no providers configured the whole block renders nothing.\n */\nfunction ProviderSignIn({\n authOrigin,\n returnTo,\n add,\n credentials,\n firstParty,\n providerHref,\n first = false,\n}: {\n authOrigin: string;\n returnTo: string;\n add: boolean;\n credentials: RequestCredentials;\n /** See OcSignInProps.mode: buttons only where the site starts them. */\n firstParty: boolean;\n providerHref?: (providerId: string) => string;\n /**\n * Render this block at the TOP of the ceremony (above the wallet/email\n * panel) rather than the default bottom. The \"or\" divider then sits\n * below the provider buttons, and the block reserves space beneath\n * itself instead of above. Set by {@link OcSignIn} when\n * `providersFirst` is on.\n */\n first?: boolean;\n}): React.ReactElement | null {\n // null while the list loads; [] once the host says none are configured.\n const [providers, setProviders] = React.useState<OAuthProviderEntry[] | null>(null);\n // A provider sign-in redirects THROUGH the auth host, so its final\n // redirect must carry an ABSOLUTE return target — a bare path would\n // resolve against ochk.io and strand a subdomain user there. The\n // origin is only knowable client-side.\n const [origin, setOrigin] = React.useState('');\n // A first-party site with no start route of its own has no provider path.\n const hidden = firstParty && !providerHref;\n\n React.useEffect(() => {\n setOrigin(window.location.origin);\n if (hidden) return;\n let cancelled = false;\n fetch(`${authOrigin}/api/auth/providers`, { credentials })\n .then((r) => (r.ok ? (r.json() as Promise<{ providers?: OAuthProviderEntry[] }>) : null))\n .then((body) => {\n if (!cancelled) setProviders(body?.providers ?? []);\n })\n .catch(() => {\n // auth host unreachable — no provider buttons; the\n // BIP-322 / email-OTP paths are unaffected.\n if (!cancelled) setProviders([]);\n });\n return () => {\n cancelled = true;\n };\n }, [authOrigin, credentials, hidden]);\n\n if (hidden) return null;\n if (providers !== null && providers.length === 0) return null;\n\n // An absolute family URL (the add-another-account round trip back to\n // a consumer subdomain) is carried verbatim through the OAuth hop;\n // a relative path is anchored to THIS page's origin.\n // It rides in the provider's start URL, so never with its fragment.\n const providerReturnTo = stripFragment(\n returnTo.startsWith('/') ? (origin ? `${origin}${returnTo}` : returnTo) : returnTo\n );\n const line = { flex: 1, height: 1, background: 'var(--border, #27272a)' } as const;\n // Vertical rhythm. The \"or\" divider is a horizontal rule between two\n // blocks, so it must sit EQUIDISTANT from both — otherwise it reads as\n // belonging to whichever block it is closer to. It previously ran\n // 14px above / 4px below (plus 4px of container margin), which glued the\n // rule to the panel beneath it and left the provider buttons floating.\n // BLOCK_GAP matches the tab row's own 16px marginBottom so the whole\n // ceremony sits on one 4px-scale rhythm rather than an ad-hoc\n // 0/4/8/14/16/20 mix.\n const BLOCK_GAP = 16;\n const BUTTON_GAP = 8; // siblings group tighter than blocks separate\n const divider = (\n <div\n style={{\n display: 'flex',\n alignItems: 'center',\n gap: 10,\n // Symmetric in both orderings: in `first` mode the divider\n // owns the gap on both sides; at the bottom the container's\n // marginTop owns the gap above, so the divider only pads below.\n margin: first ? `${BLOCK_GAP}px 0` : `0 0 ${BLOCK_GAP}px`,\n color: 'var(--muted-foreground, #a1a1aa)',\n fontFamily: SIGNIN_FONT,\n fontSize: signinRoleSize('hint', signinSize(10)),\n letterSpacing: signinTracking('0.16em'),\n textTransform: SIGNIN_CASE,\n }}\n >\n <span style={line} />\n or\n <span style={line} />\n </div>\n );\n const buttonStyle = (i: number): React.CSSProperties => ({\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n gap: 10,\n boxSizing: 'border-box',\n width: '100%',\n minHeight: 44,\n marginTop: i === 0 ? 0 : BUTTON_GAP,\n padding: '0.6rem 0.875rem',\n border: '1px solid var(--border, #27272a)',\n borderRadius: SIGNIN_BUTTON_RADIUS,\n background: 'transparent',\n color: 'var(--muted-foreground, #a1a1aa)',\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(12),\n textDecoration: 'none',\n });\n // While the list loads, hold the space two buttons will take. Rendering\n // nothing and then inserting them above the form pushed it down after\n // first paint: most of /signin?add=1's CLS (0.11 at 390).\n if (providers === null) {\n if (!first) return null;\n return (\n <div data-oc-signin-providers=\"\" aria-hidden=\"true\">\n {[0, 1].map((i) => (\n <div key={i} style={{ ...buttonStyle(i), visibility: 'hidden' }}>\n &nbsp;\n </div>\n ))}\n {divider}\n </div>\n );\n }\n const buttons = providers.map((p, i) => (\n <a\n key={p.id}\n href={\n providerHref\n ? providerHref(p.id)\n : `${authOrigin}/api/auth/${p.id}/start?return_to=${encodeURIComponent(\n providerReturnTo\n )}${add ? '&add=1' : ''}`\n }\n data-oc-signin-provider={p.id}\n style={buttonStyle(i)}\n >\n <ProviderIcon id={p.id} />\n <span>{p.label}</span>\n </a>\n ));\n // first → providers lead: buttons, then the \"or\" divider before the\n // wallet/email panel below. Otherwise the canonical bottom placement:\n // divider, then buttons.\n return (\n <div\n data-oc-signin-providers=\"\"\n style={first ? { marginBottom: 0 } : { marginTop: BLOCK_GAP }}\n >\n {first ? (\n <>\n {buttons}\n {divider}\n </>\n ) : (\n <>\n {divider}\n {buttons}\n </>\n )}\n </div>\n );\n}\n\n/* --- wallet flow --- */\n\ninterface FlowProps {\n authOrigin: string;\n /** Multi-account add-mode · see OcSignIn.add. */\n add?: boolean;\n /** `omit` in first-party mode · see OcSignInProps.mode. */\n credentials: RequestCredentials;\n onSuccess: (account: OcAccount, token?: string) => void;\n}\n\ninterface WalletFlowProps extends FlowProps {\n audience: string;\n}\n\ntype AdapterShape = {\n detectWallets: () => Array<{ id: string; name: string; detected: boolean }>;\n getSigner: (id: string, opts: { address: string }) => (message: string) => Promise<string>;\n walletFailureText: (err: unknown, opts?: { paste?: boolean }) => string;\n};\n\nasync function loadAdapter(): Promise<AdapterShape> {\n // A dependency, not an optional peer: this is a *static* dynamic import, so\n // the consumer's bundler must resolve it at build time and a missing package\n // fails the build. The old `Function('m','return import(m)')` trick produced\n // a runtime bare-specifier `import()` the browser cannot resolve without an\n // import map — which is what broke sign-in everywhere.\n return (await import('@orangecheck/wallet-adapter')) as unknown as AdapterShape;\n}\n\ntype DetectedWallet = { id: string; name: string };\n\nasync function detectBrowserWallets(): Promise<DetectedWallet[]> {\n return (await loadAdapter())\n .detectWallets()\n .filter((w) => w.detected && w.id !== 'manual')\n .map((w) => ({ id: w.id, name: w.name }));\n}\n\nfunction WalletFlow({\n authOrigin,\n audience,\n add,\n credentials,\n onSuccess,\n}: WalletFlowProps): React.ReactElement {\n const uid = React.useId();\n const ids = {\n address: `${uid}-address`,\n wallet: `${uid}-wallet`,\n message: `${uid}-message`,\n signature: `${uid}-signature`,\n };\n const [address, setAddress] = React.useState('');\n const [error, setError] = React.useState<string | null>(null);\n const [submitting, setSubmitting] = React.useState(false);\n // Browser wallets on this device; null until the adapter has loaded.\n const [wallets, setWallets] = React.useState<DetectedWallet[] | null>(null);\n const [walletId, setWalletId] = React.useState<string | null>(null);\n // No browser wallet (every phone, Sparrow, hardware wallets): show the\n // challenge to sign elsewhere and take the signature back. This used to\n // end in \"no BIP-322 wallet extension detected · install one and refresh\".\n const [manual, setManual] = React.useState<{ message: string; nonce: string } | null>(null);\n const [pasted, setPasted] = React.useState('');\n const [copied, setCopied] = React.useState<'idle' | 'copied' | 'failed'>('idle');\n\n React.useEffect(() => {\n let cancelled = false;\n detectBrowserWallets()\n .then((found) => {\n if (cancelled) return;\n setWallets(found);\n setWalletId(found[0]?.id ?? null);\n })\n .catch(() => {\n if (!cancelled) setWallets([]);\n });\n return () => {\n cancelled = true;\n };\n }, []);\n\n async function fetchChallenge(addr: string): Promise<{ message: string; nonce: string }> {\n const res = await fetch(\n `${authOrigin}/api/challenge?addr=${encodeURIComponent(addr)}&audience=${encodeURIComponent(audience)}&purpose=login`\n );\n const json = (await res.json()) as { message?: string; nonce?: string; error?: string };\n if (!res.ok || !json.message || !json.nonce) {\n throw new Error(json.error ?? 'challenge_failed');\n }\n return { message: json.message, nonce: json.nonce };\n }\n\n /**\n * POST the signature. A browser wallet signs BIP-322, so that path names\n * the scheme. A pasted signature may be legacy BIP-137 (Electrum, most\n * hardware wallets on a `1…` address), which the verifier only detects\n * when no scheme is named, so the manual path leaves it out.\n */\n async function submitSignature(\n message: string,\n nonce: string,\n signature: string,\n scheme: 'bip322' | undefined\n ): Promise<void> {\n // Family-CORS lets the .ochk.io session cookie land cross-origin.\n const res = await fetch(`${authOrigin}/api/auth/signin`, {\n method: 'POST',\n credentials,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n message,\n signature,\n ...(scheme ? { scheme } : {}),\n expectedNonce: nonce,\n expectedAudience: audience,\n expectedPurpose: 'login',\n // Multi-account add-mode · the auth host preserves the current\n // roster_id when set, instead of minting a fresh one.\n ...(add ? { add: true } : {}),\n }),\n });\n const json = (await res.json()) as SigninJson;\n if (!res.ok || !('ok' in json) || !json.ok || !json.account) {\n throw new Error(\n ('reason' in json && json.reason) ||\n ('error' in json && json.error) ||\n 'verify_failed'\n );\n }\n onSuccess(json.account, json.token);\n }\n\n async function signIn(e: React.FormEvent): Promise<void> {\n e.preventDefault();\n const addr = address.trim();\n if (!addr) {\n setError('Paste your Bitcoin address.');\n return;\n }\n if (!looksLikeBitcoinAddress(addr)) {\n setError(humanSigninError('bad_request', 'wallet'));\n return;\n }\n setError(null);\n setSubmitting(true);\n let step: 'challenge' | 'submit' = 'challenge';\n try {\n // Submitting before detection finished must not send someone with\n // an extension to the paste-a-signature path.\n let id = walletId;\n if (wallets === null) {\n const found = await detectBrowserWallets().catch(() => []);\n setWallets(found);\n id = found[0]?.id ?? null;\n setWalletId(id);\n }\n const challenge = await fetchChallenge(addr);\n if (!id) {\n setManual(challenge);\n setSubmitting(false);\n return;\n }\n const adapter = await loadAdapter();\n let signature: string;\n try {\n signature = await adapter.getSigner(id, { address: addr })(challenge.message);\n } catch (err) {\n // The wallet's own words never reach a person; the adapter names the cause.\n setError(adapter.walletFailureText(err));\n setSubmitting(false);\n return;\n }\n step = 'submit';\n await submitSignature(challenge.message, challenge.nonce, signature, 'bip322');\n } catch (err) {\n setError(failureMessage(err, 'wallet', step, authOrigin));\n setSubmitting(false);\n }\n }\n\n async function signInManual(e: React.FormEvent): Promise<void> {\n e.preventDefault();\n if (!manual) return;\n const sig = pasted.trim();\n if (!sig) {\n setError('Paste the signature your wallet gave you.');\n return;\n }\n setError(null);\n setSubmitting(true);\n try {\n await submitSignature(manual.message, manual.nonce, sig, undefined);\n } catch (err) {\n setError(failureMessage(err, 'wallet', 'submit', authOrigin));\n setSubmitting(false);\n }\n }\n\n async function copyMessage(): Promise<void> {\n if (!manual) return;\n try {\n await navigator.clipboard.writeText(manual.message);\n setCopied('copied');\n } catch {\n setCopied('failed');\n }\n }\n\n if (manual) {\n return (\n <form onSubmit={signInManual} data-oc-signin-wallet-manual=\"\">\n <FlowHeader>\n In your wallet, use “sign message” (BIP-322) with {address.trim()}, sign the\n text below, and paste the signature back here. Nothing is spent.\n </FlowHeader>\n <Label htmlFor={ids.message}>Message to sign</Label>\n <textarea\n data-oca-field=\"\"\n id={ids.message}\n readOnly\n value={manual.message}\n rows={5}\n onFocus={(e) => e.currentTarget.select()}\n style={{ ...inputStyle, resize: 'vertical' }}\n />\n <button type=\"button\" onClick={() => void copyMessage()} style={linkButtonStyle}>\n {copied === 'copied' ? 'Copied' : 'Copy message'}\n </button>\n {copied === 'failed' && (\n <Hint>Couldn&apos;t copy here. Select the text above and copy it.</Hint>\n )}\n <div style={{ marginTop: 14 }}>\n <Label htmlFor={ids.signature}>Signature</Label>\n <textarea\n data-oca-field=\"\"\n id={ids.signature}\n value={pasted}\n onChange={(e) => {\n setPasted(e.target.value);\n if (error) setError(null);\n }}\n rows={3}\n placeholder=\"paste the signature\"\n spellCheck={false}\n autoFocus\n aria-invalid={error ? true : undefined}\n style={{ ...inputStyle, resize: 'vertical' }}\n />\n </div>\n {error && <ErrorLine>{error}</ErrorLine>}\n <button\n type=\"submit\"\n disabled={submitting || !pasted.trim()}\n style={submitStyle(submitting || !pasted.trim())}\n >\n {submitting ? 'Checking…' : <Submit>Sign me in</Submit>}\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setManual(null);\n setPasted('');\n setError(null);\n setCopied('idle');\n }}\n style={linkButtonStyle}\n >\n Use a different address\n </button>\n </form>\n );\n }\n\n const noBrowserWallet = wallets !== null && wallets.length === 0;\n return (\n <form onSubmit={signIn} data-oc-signin-wallet=\"\">\n <FlowHeader>\n Your wallet signs a short message to prove you hold the address. Nothing is spent\n and your key never leaves the wallet. Works with browser wallets, or any wallet that\n can sign a message, like Sparrow.\n </FlowHeader>\n <Label htmlFor={ids.address}>Bitcoin address</Label>\n <input\n data-oca-field=\"\"\n id={ids.address}\n type=\"text\"\n value={address}\n onChange={(e) => {\n setAddress(e.target.value);\n if (error) setError(null);\n }}\n placeholder=\"bc1q… · 3… · 1…\"\n autoComplete=\"off\"\n autoCapitalize=\"none\"\n spellCheck={false}\n required\n aria-invalid={error ? true : undefined}\n style={inputStyle}\n />\n {wallets && wallets.length > 1 && (\n <div style={{ marginTop: 12 }}>\n <Label htmlFor={ids.wallet}>Sign with</Label>\n <select\n id={ids.wallet}\n value={walletId ?? ''}\n onChange={(e) => setWalletId(e.target.value)}\n style={inputStyle}\n >\n {wallets.map((w) => (\n <option key={w.id} value={w.id}>\n {w.name}\n </option>\n ))}\n </select>\n </div>\n )}\n {error && <ErrorLine>{error}</ErrorLine>}\n <button\n type=\"submit\"\n disabled={submitting || !address.trim()}\n style={submitStyle(submitting || !address.trim())}\n >\n {submitting\n ? 'Waiting for wallet…'\n : noBrowserWallet\n ? <Submit>Get the message to sign</Submit>\n : <Submit>Sign in with wallet</Submit>}\n </button>\n {noBrowserWallet && (\n <Hint>No browser wallet found here. You can sign with any wallet and paste.</Hint>\n )}\n </form>\n );\n}\n\n/* --- email flow --- */\n\ntype EmailStage = 'enter' | 'code';\n\nfunction EmailFlow({ authOrigin, add, credentials, onSuccess }: FlowProps): React.ReactElement {\n const uid = React.useId();\n const [stage, setStage] = React.useState<EmailStage>('enter');\n const [email, setEmail] = React.useState('');\n const [emailError, setEmailError] = React.useState<string | null>(null);\n const [code, setCode] = React.useState('');\n const [codeError, setCodeError] = React.useState<string | null>(null);\n const [token, setToken] = React.useState<string | null>(null);\n const [submitting, setSubmitting] = React.useState(false);\n const [resent, setResent] = React.useState(false);\n\n /** Ask the host to email a code. Returns a human error, or null on success. */\n async function sendCode(address: string): Promise<string | null> {\n try {\n const res = await fetch(`${authOrigin}/api/auth/email-otp/start`, {\n method: 'POST',\n credentials,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ email: address }),\n });\n const json = (await res.json()) as { ok?: boolean; token?: string; reason?: string };\n if (!res.ok || !json.token) return humanSigninError(json.reason ?? 'server_error', 'email');\n setToken(json.token);\n return null;\n } catch {\n return networkError(authOrigin);\n }\n }\n\n async function start(e: React.FormEvent): Promise<void> {\n e.preventDefault();\n const trimmed = email.trim().toLowerCase();\n if (!trimmed || !/.+@.+\\..+/.test(trimmed)) {\n setEmailError(humanSigninError('invalid_email', 'email'));\n return;\n }\n setEmailError(null);\n setSubmitting(true);\n const err = await sendCode(trimmed);\n setSubmitting(false);\n if (err) {\n setEmailError(err);\n return;\n }\n setEmail(trimmed);\n setStage('code');\n }\n\n async function resend(): Promise<void> {\n setCodeError(null);\n setResent(false);\n setSubmitting(true);\n const err = await sendCode(email);\n setSubmitting(false);\n setCode('');\n if (err) setCodeError(err);\n else setResent(true);\n }\n\n async function verify(e: React.FormEvent): Promise<void> {\n e.preventDefault();\n if (!token) return;\n if (code.length !== 6) {\n setCodeError('The code is 6 digits.');\n return;\n }\n setCodeError(null);\n setSubmitting(true);\n try {\n const res = await fetch(`${authOrigin}/api/auth/email-otp/verify`, {\n method: 'POST',\n credentials,\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n email,\n code,\n token,\n // Multi-account add-mode · see WalletFlow.\n ...(add ? { add: true } : {}),\n }),\n });\n const json = (await res.json()) as SigninJson;\n if (!res.ok || !('ok' in json) || !json.ok || !json.account) {\n throw new Error(\n ('reason' in json && json.reason) ||\n ('error' in json && json.error) ||\n 'server_error'\n );\n }\n onSuccess(json.account, json.token);\n } catch (err) {\n setCodeError(failureMessage(err, 'email', 'submit', authOrigin));\n } finally {\n setSubmitting(false);\n }\n }\n\n if (stage === 'enter') {\n return (\n <form onSubmit={start} data-oc-signin-email=\"\">\n <FlowHeader>\n We email you a 6-digit code. No password, and no wallet needed to start. Add a\n Bitcoin address any time: a way in that only you hold.\n </FlowHeader>\n <Label htmlFor={`${uid}-email`}>Email</Label>\n <input\n data-oca-field=\"\"\n id={`${uid}-email`}\n type=\"email\"\n autoComplete=\"email\"\n value={email}\n onChange={(e) => {\n setEmail(e.target.value);\n if (emailError) setEmailError(null);\n }}\n placeholder=\"you@example.com\"\n required\n aria-invalid={emailError ? true : undefined}\n style={inputStyle}\n />\n {emailError && <ErrorLine>{emailError}</ErrorLine>}\n <button type=\"submit\" disabled={submitting} style={submitStyle(submitting)}>\n {submitting ? 'Sending…' : <Submit>Send one-time code</Submit>}\n </button>\n </form>\n );\n }\n\n return (\n <form onSubmit={verify} data-oc-signin-email-verify=\"\">\n <FlowHeader>\n {resent ? 'Sent a new code to ' : 'Sent a 6-digit code to '}\n <span style={{ color: 'var(--foreground, #fafafa)' }}>{email}</span>. It expires in\n 10 minutes.\n </FlowHeader>\n <Label htmlFor={`${uid}-code`}>One-time code</Label>\n <input\n data-oca-field=\"\"\n id={`${uid}-code`}\n type=\"text\"\n inputMode=\"numeric\"\n pattern=\"\\d{6}\"\n maxLength={6}\n autoComplete=\"one-time-code\"\n value={code}\n onChange={(e) => {\n setCode(e.target.value.replace(/\\D/g, '').slice(0, 6));\n if (codeError) setCodeError(null);\n }}\n placeholder=\"6 digits\"\n required\n autoFocus\n aria-invalid={codeError ? true : undefined}\n style={{ ...inputStyle, letterSpacing: '0.4em', fontSize: 16 }}\n />\n {codeError && <ErrorLine>{codeError}</ErrorLine>}\n <button\n type=\"submit\"\n disabled={submitting || code.length !== 6}\n style={submitStyle(submitting || code.length !== 6)}\n >\n {submitting ? 'Verifying…' : <Submit>Verify · sign me in</Submit>}\n </button>\n <div>\n <button\n type=\"button\"\n onClick={() => void resend()}\n disabled={submitting}\n style={linkButtonStyle}\n >\n Send a new code\n </button>\n <button\n type=\"button\"\n onClick={() => {\n setStage('enter');\n setCode('');\n setToken(null);\n setResent(false);\n }}\n style={linkButtonStyle}\n >\n Use a different email\n </button>\n </div>\n </form>\n );\n}\n\n/* --- shared subcomponents --- */\n\n/** A submit label and its arrow, which a site can hide (--oc-signin-arrow). */\nfunction Submit({ children }: { children: string }): React.ReactElement {\n return (\n <>\n {children}\n <span aria-hidden=\"true\" style={{ display: SIGNIN_ARROW }}>\n {' →'}\n </span>\n </>\n );\n}\n\nfunction FlowHeader({\n label,\n children,\n}: {\n /** Omitted where the active tab already names the method. */\n label?: string;\n children: React.ReactNode;\n}): React.ReactElement {\n return (\n <div style={{ marginBottom: 16 }}>\n {label && (\n <div\n style={{\n color: 'var(--primary, #f97316)',\n fontFamily: SIGNIN_FONT,\n fontSize: signinSize(10),\n letterSpacing: signinTracking('0.18em'),\n textTransform: SIGNIN_CASE,\n marginBottom: 6,\n }}\n >\n {label}\n </div>\n )}\n <p\n style={{\n color: 'var(--muted-foreground, #a1a1aa)',\n fontSize: signinRoleSize('text', '13px'),\n lineHeight: 1.55,\n margin: 0,\n }}\n >\n {children}\n </p>\n </div>\n );\n}\n\nconst linkButtonStyle: React.CSSProperties = {\n display: 'inline-flex',\n alignItems: 'center',\n minHeight: 44,\n marginTop: 4,\n marginRight: 16,\n background: 'transparent',\n border: 'none',\n color: 'var(--muted-foreground, #a1a1aa)',\n fontFamily: SIGNIN_FONT,\n fontSize: signinRoleSize('button', signinSize(11)),\n letterSpacing: signinTracking('0.12em'),\n textTransform: SIGNIN_CASE,\n cursor: 'pointer',\n padding: 0,\n};\n\nfunction Label({\n htmlFor,\n children,\n}: {\n htmlFor: string;\n children: React.ReactNode;\n}): React.ReactElement {\n return (\n <label\n htmlFor={htmlFor}\n style={{\n display: 'block',\n color: 'var(--foreground, #fafafa)',\n fontFamily: SIGNIN_FONT,\n fontSize: signinRoleSize('label', signinSize(11)),\n letterSpacing: signinTracking('0.12em'),\n textTransform: SIGNIN_CASE,\n marginBottom: 6,\n }}\n >\n {children}\n </label>\n );\n}\n\nconst inputStyle: React.CSSProperties = {\n width: '100%',\n padding: '0.55rem 0.75rem',\n background: 'var(--background, #0a0a0a)',\n color: 'var(--foreground, #fafafa)',\n border: '1px solid var(--input, #27272a)',\n borderRadius: signinRadius(6),\n fontFamily: SIGNIN_FONT,\n // 16px — never below it. A sub-16px font-size makes iOS Safari\n // auto-zoom the viewport on focus; the family rule is >=16px on\n // every form field. Desktop reads fine at 16 too.\n fontSize: 16,\n};\n\nconst linkAlsoStyle: React.CSSProperties = {\n display: 'flex',\n alignItems: 'center',\n // The whole row is the checkbox's hit area; the 13px box alone was not.\n minHeight: 44,\n gap: 8,\n marginTop: 16,\n paddingTop: 14,\n borderTop: '1px solid var(--border, #27272a)',\n color: 'var(--muted-foreground, #a1a1aa)',\n fontFamily: SIGNIN_FONT,\n fontSize: signinRoleSize('text', signinSize(12)),\n lineHeight: 1.5,\n cursor: 'pointer',\n};\n\nfunction submitStyle(disabled: boolean): React.CSSProperties {\n return {\n marginTop: 12,\n padding: '0.6rem 1rem',\n background: 'var(--primary, #f97316)',\n color: 'var(--primary-foreground, #0a0a0a)',\n border: 'none',\n borderRadius: SIGNIN_BUTTON_RADIUS,\n fontFamily: SIGNIN_FONT,\n fontSize: signinRoleSize('button', signinSize(11)),\n letterSpacing: signinTracking('0.14em'),\n textTransform: SIGNIN_CASE,\n fontWeight: SIGNIN_STRONG,\n cursor: disabled ? 'not-allowed' : 'pointer',\n opacity: disabled ? 0.5 : 1,\n width: '100%',\n };\n}\n\nfunction ErrorLine({ children }: { children: React.ReactNode }): React.ReactElement {\n return (\n <p\n role=\"alert\"\n style={{\n marginTop: 6,\n marginBottom: 0,\n color: 'var(--destructive, #ef4444)',\n fontFamily: SIGNIN_FONT,\n fontSize: signinRoleSize('hint', signinSize(11)),\n letterSpacing: '0.04em',\n }}\n >\n {children}\n </p>\n );\n}\n\nfunction Hint({ children }: { children: React.ReactNode }): React.ReactElement {\n return (\n <p\n style={{\n marginTop: 12,\n marginBottom: 0,\n color: 'var(--muted-foreground, #a1a1aa)',\n fontFamily: SIGNIN_FONT,\n fontSize: signinRoleSize('hint', '10.5px'),\n lineHeight: 1.55,\n }}\n >\n {children}\n </p>\n );\n}\n"],"mappings":"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","names":["SigninTab"]}
@@ -1 +0,0 @@
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 data-oca-field=\"\"\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 data-oca-field=\"\"\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 data-oca-field=\"\"\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":[]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/bond.tsx"],"sourcesContent":["'use client';\n\n/**\n * OcBond · back the signed-in account with bitcoin (oc-attest-protocol\n * BOND.md). The person signs for one coin they hold. It stays in their wallet,\n * and spending it ends the backing. Apps they sign in to see floors and a\n * number unique to that app, never the coin. Optional: every account signs in\n * without one.\n *\n * Runs against the auth host's /api/auth/bond with the family cookie, like\n * OcLinkedIdentities, so any *.ochk.io site can mount it. A change re-mints\n * the session there, and a tab pinned to this account re-pins with the fresh\n * token; `onChange` lets the site refresh its session view.\n */\nimport * as React from 'react';\n\nimport { SIGNIN_FONT } from './appearance';\nimport {\n bodyStyle,\n dangerBtnStyle,\n ErrorLine,\n FormButtons,\n ghostBtnStyle,\n hintStyle,\n inputStyle,\n LazyWalletButton,\n panelStyle,\n primaryBtnStyle,\n SectionLabel,\n V,\n} from './linked-identities';\nimport { readTabSession, writeTabSession } from './tab-session';\n\nconst DEFAULT_AUTH_ORIGIN = 'https://ochk.io';\nconst MIN_CONFIRMATIONS = 6;\n/** Smaller coins meet no floor, so the auth host refuses them (`coin_too_small`). */\nconst MIN_SATS = 1_000;\n\ninterface Floors {\n sats_floor: number;\n days_floor: number;\n}\n\n/** The account's bond as the auth host shows it to the account itself. */\nexport interface OcBondView extends Floors {\n address: string;\n outpoint: string;\n sats: number;\n confirmed_height: number;\n checked_at: string;\n checked_height: number;\n created_at: string;\n}\n\ninterface Coin extends Floors {\n outpoint: string;\n sats: number;\n age_blocks: number;\n}\n\nexport interface OcBondProps {\n /** The auth host. Default https://ochk.io; pass the current origin on ochk.io itself. */\n authOrigin?: string;\n /** After a bond is made or released, so the site can refresh its session. */\n onChange?: (bond: OcBondView | null) => void;\n className?: string;\n}\n\nconst n = (v: number) => v.toLocaleString('en-US');\n\n/** \"at least 100,000 sats, held 90 days or more\". */\nexport function bondFloorsText(f: Floors): string {\n const sats = f.sats_floor > 0 ? `at least ${n(f.sats_floor)} sats` : 'under 1,000 sats';\n const days = f.days_floor > 0 ? `held ${f.days_floor} days or more` : 'held under 7 days';\n return `${sats}, ${days}`;\n}\n\nconst short = (outpoint: string) => {\n const [txid = '', vout = ''] = outpoint.split(':');\n return `${txid.slice(0, 8)}…${txid.slice(-4)}:${vout}`;\n};\n\nconst usable = (c: Coin) => c.sats >= MIN_SATS && c.age_blocks >= MIN_CONFIRMATIONS;\n\nconst dustText = (count: number) =>\n `${n(count)} ${count === 1 ? 'coin' : 'coins'} under ${n(MIN_SATS)} sats, ` +\n 'too small to back an account';\n\nconst REFUSAL: Record<string, string> = {\n invalid_address: \"that isn't a Bitcoin address we can read\",\n chain_unavailable: \"couldn't read the chain just now · nothing changed, try again in a minute\",\n sig_invalid:\n \"that signature isn't from this coin's address · sign with the wallet that holds it\",\n bond_expired: 'the message expired · start again',\n bond_not_yet_valid: \"your device's clock looks wrong · check it and start again\",\n bond_subject_mismatch: 'that message was for another account · start again',\n bond_malformed: 'the signed message changed on the way · start again',\n bond_window_too_long: \"that message's signing window is too long · start again\",\n coin_not_found: \"that coin doesn't exist on-chain\",\n coin_address_mismatch: \"that coin isn't at this address\",\n coin_unconfirmed: \"that coin isn't confirmed yet\",\n coin_too_recent: 'that coin needs six confirmations first, about an hour',\n coin_spent: 'that coin has been spent · pick one still in your wallet',\n coin_too_small: `that coin is under ${n(MIN_SATS)} sats, too small to back an account`,\n coin_backs_another_account: 'that coin already backs another orangecheck account',\n coin_backed_another_recently:\n 'that coin backed another account until recently · ' +\n 'it can back this one 30 days after it was released',\n not_authenticated: 'your session ended · sign in again',\n rate_limited: 'too many tries · wait a minute, then try again',\n};\n\n/** A refusal from /api/auth/bond, in the component's voice. Not exported from the index. */\nexport const bondRefusal = (reason?: string): string =>\n (reason && REFUSAL[reason]) || \"your backing didn't save · nothing changed, try again\";\n\n/* Small print stays legible: 12px or more, and never dimmed below the muted tone. */\nconst smallStyle: React.CSSProperties = { ...bodyStyle, fontSize: 12, margin: 0 };\nconst explainerStyle: React.CSSProperties = {\n ...hintStyle,\n fontSize: 12,\n lineHeight: 1.55,\n opacity: 1,\n margin: 0,\n};\nconst fieldLabelStyle: React.CSSProperties = {\n display: 'block',\n color: V.foreground,\n fontSize: 13,\n fontWeight: 500,\n margin: '0 0 4px',\n};\nconst addressStyle: React.CSSProperties = { wordBreak: 'break-all', color: V.foreground };\n\nfunction coinStyle(picked: boolean, young: boolean): React.CSSProperties {\n return {\n background: V.card,\n border: `1px ${young ? 'dashed' : 'solid'} ${picked ? V.primary : V.border}`,\n padding: '0.75rem',\n display: 'flex',\n gap: 10,\n alignItems: 'flex-start',\n cursor: young ? 'not-allowed' : 'pointer',\n };\n}\n\n/** The account's bond, or null. Throws when the auth host can't be read. */\nexport async function fetchOcBond(opts?: { authOrigin?: string }): Promise<OcBondView | null> {\n const r = await fetch(`${opts?.authOrigin ?? DEFAULT_AUTH_ORIGIN}/api/auth/bond`, {\n credentials: 'include',\n });\n const j = (await r.json().catch(() => ({}))) as { ok?: boolean; bond?: OcBondView | null };\n if (!r.ok || !j.ok) throw new Error(`http_${r.status}`);\n return j.bond ?? null;\n}\n\nexport function OcBond({\n authOrigin = DEFAULT_AUTH_ORIGIN,\n onChange,\n className,\n}: OcBondProps): React.ReactElement {\n const [bond, setBond] = React.useState<OcBondView | null | undefined>(undefined);\n const [loadErr, setLoadErr] = React.useState(false);\n const [nonce, setNonce] = React.useState(0);\n const [adding, setAdding] = 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 React.useEffect(() => {\n let alive = true;\n fetchOcBond({ authOrigin }).then(\n (b) => alive && setBond(b),\n () => alive && setLoadErr(true)\n );\n return () => {\n alive = false;\n };\n }, [authOrigin, nonce]);\n\n const call = React.useCallback(\n async (method: string, body?: unknown) => {\n const r = await fetch(`${authOrigin}/api/auth/bond`, {\n method,\n credentials: 'include',\n headers: body ? { 'Content-Type': 'application/json' } : undefined,\n body: body ? JSON.stringify(body) : undefined,\n });\n const j = (await r.json().catch(() => ({}))) as Record<string, unknown> & {\n ok?: boolean;\n reason?: string;\n token?: unknown;\n };\n // The host minted for the account this tab shows; keep the pin current.\n const pin = readTabSession();\n if (pin && r.ok && typeof j.token === 'string') {\n writeTabSession({ token: j.token, didOc: pin.didOc });\n }\n return { ok: r.ok && j.ok === true, ...j };\n },\n [authOrigin]\n );\n\n async function release(): Promise<void> {\n setBusy(true);\n setErr(null);\n const j = await call('DELETE').catch(() => ({ ok: false, reason: 'network' }));\n setBusy(false);\n if (!j.ok) return setErr(bondRefusal(j.reason));\n setConfirming(false);\n setBond(null);\n onChange?.(null);\n }\n\n if (loadErr) {\n return (\n <div className={className} data-oc-bond=\"\" style={panelStyle('warn')} role=\"alert\">\n <p style={bodyStyle}>couldn&apos;t read your backing · nothing was changed</p>\n <button\n type=\"button\"\n style={ghostBtnStyle(false)}\n onClick={() => {\n setLoadErr(false);\n setNonce((x) => x + 1);\n }}\n >\n try again\n </button>\n </div>\n );\n }\n if (bond === undefined) {\n return (\n <div className={className} data-oc-bond=\"\" style={panelStyle()}>\n <span style={hintStyle}>reading your backing…</span>\n </div>\n );\n }\n\n return (\n <div className={className} data-oc-bond=\"\" style={{ display: 'grid', gap: 16 }}>\n {bond && !adding ? (\n <div style={panelStyle('success')}>\n <SectionLabel tone=\"success\">§ backed by bitcoin</SectionLabel>\n <p style={{ ...bodyStyle, color: V.foreground }}>\n apps see a coin {bondFloorsText(bond)}.\n </p>\n <p style={bodyStyle}>\n coin {short(bond.outpoint)}, {n(bond.sats)} sats · last found unspent at\n block {n(bond.checked_height)} · only you and orangecheck see this line\n </p>\n {confirming ? (\n <div role=\"group\" aria-label=\"stop backing\" data-oc-bond-confirm=\"\">\n <p style={{ ...bodyStyle, color: V.foreground, margin: 0 }}>\n stop backing? apps that ask for a backed account may stop letting\n you in.\n </p>\n <FormButtons>\n <button\n type=\"button\"\n style={dangerBtnStyle(busy)}\n disabled={busy}\n onClick={() => void release()}\n >\n stop backing\n </button>\n <button\n type=\"button\"\n style={ghostBtnStyle(busy)}\n disabled={busy}\n autoFocus\n onClick={() => {\n setErr(null);\n setConfirming(false);\n }}\n >\n keep\n </button>\n </FormButtons>\n </div>\n ) : (\n <FormButtons>\n <button\n type=\"button\"\n style={ghostBtnStyle(false)}\n onClick={() => {\n setErr(null);\n setAdding(true);\n }}\n >\n use another coin\n </button>\n <button\n type=\"button\"\n style={dangerBtnStyle(false)}\n onClick={() => setConfirming(true)}\n >\n stop backing\n </button>\n </FormButtons>\n )}\n {err ? <ErrorLine>{err}</ErrorLine> : null}\n </div>\n ) : null}\n {!bond && !adding ? (\n <div style={panelStyle()}>\n <SectionLabel>§ not backed</SectionLabel>\n <p style={bodyStyle}>\n your account works everywhere as it is. some apps let in only accounts\n backed by a coin you hold. it stays in your wallet, and apps never see\n which coin.\n </p>\n <button\n type=\"button\"\n style={primaryBtnStyle(false)}\n onClick={() => setAdding(true)}\n >\n back your account\n </button>\n </div>\n ) : null}\n {adding ? (\n <BondForm\n call={call}\n onCancel={() => setAdding(false)}\n onDone={(b) => {\n setBond(b);\n setAdding(false);\n onChange?.(b);\n }}\n />\n ) : null}\n <p style={explainerStyle}>\n Apps see only rough floors, like “at least 100,000 sats, held 30 days or\n more”, and a number unique to that app. Never the coin, your address or your\n balance. An app can block that number; only moving the coin gets past it, and a\n moved coin starts its age again.\n </p>\n </div>\n );\n}\n\ntype Call = (\n method: string,\n body?: unknown\n) => Promise<Record<string, unknown> & { ok: boolean; reason?: string }>;\n\nfunction BondForm({\n call,\n onCancel,\n onDone,\n}: {\n call: Call;\n onCancel: () => void;\n onDone: (bond: OcBondView) => void;\n}): React.ReactElement {\n const [address, setAddress] = React.useState('');\n const [coins, setCoins] = React.useState<Coin[] | null>(null);\n const [chosen, setChosen] = React.useState('');\n const [message, setMessage] = React.useState<string | null>(null);\n const [busy, setBusy] = React.useState(false);\n const [err, setErr] = React.useState<string | null>(null);\n const id = React.useId();\n\n const addr = address.trim();\n const canFind = addr.length >= 14;\n\n async function run(fn: () => Promise<void>): Promise<void> {\n setBusy(true);\n setErr(null);\n try {\n await fn();\n } catch {\n setErr(bondRefusal('chain_unavailable'));\n } finally {\n setBusy(false);\n }\n }\n\n const findCoins = () =>\n run(async () => {\n const j = await call('POST', { action: 'coins', address: addr });\n if (!j.ok) return setErr(bondRefusal(j.reason));\n const list = (j.coins as Coin[]) ?? [];\n setCoins(list);\n setChosen(list.find(usable)?.outpoint ?? '');\n });\n\n const getMessage = () =>\n run(async () => {\n const j = await call('POST', { action: 'message', address: addr, outpoint: chosen });\n if (!j.ok || typeof j.message !== 'string') return setErr(bondRefusal(j.reason));\n setMessage(j.message);\n });\n\n const submit = (signature: string) =>\n run(async () => {\n const j = await call('POST', { action: 'bond', message, signature });\n if (!j.ok || !j.bond) return setErr(bondRefusal(j.reason));\n onDone(j.bond as OcBondView);\n });\n\n /** Back one step: from signing to the coin list, or from the coins to the address. */\n function back(): void {\n setErr(null);\n if (message) setMessage(null);\n else setCoins(null);\n }\n\n const error = err ? <ErrorLine>{err}</ErrorLine> : null;\n const backButton = (\n <button type=\"button\" style={ghostBtnStyle(busy)} disabled={busy} onClick={back}>\n back\n </button>\n );\n const cancelButton = (\n <button type=\"button\" style={ghostBtnStyle(busy)} disabled={busy} onClick={onCancel}>\n cancel\n </button>\n );\n\n let step: React.ReactNode;\n if (message) {\n step = (\n <>\n <p style={bodyStyle}>\n sign with the wallet that holds <span style={addressStyle}>{addr}</span>.\n signing spends nothing, and the coin stays in your wallet.\n </p>\n <LazyWalletButton\n address={addr}\n message={message}\n showManual\n onSigned={(sig) => void submit(sig)}\n onError={(e) => setErr(e.message ?? 'the wallet refused to sign')}\n />\n {error}\n <FormButtons>\n {backButton}\n {cancelButton}\n </FormButtons>\n </>\n );\n } else if (coins) {\n const eligible = coins.filter((c) => c.sats >= MIN_SATS);\n const dust = coins.length - eligible.length;\n step = (\n <>\n {coins.length === 0 ? (\n <p style={bodyStyle}>\n no confirmed coins at that address · send at least {n(MIN_SATS)} sats\n there and wait for six confirmations, about an hour. the longer a coin\n sits, the more it says.\n </p>\n ) : eligible.length === 0 ? (\n <>\n <p style={{ ...bodyStyle, color: V.foreground }}>{dustText(dust)}</p>\n <p style={bodyStyle}>\n send at least {n(MIN_SATS)} sats there in one payment and wait for six\n confirmations, about an hour. coins aren&apos;t added together.\n </p>\n </>\n ) : (\n <>\n <p id={`${id}-pick`} style={bodyStyle}>\n pick one coin · coins aren&apos;t added together\n </p>\n <div\n role=\"radiogroup\"\n aria-labelledby={`${id}-pick`}\n style={{ display: 'grid', gap: 8 }}\n >\n {eligible.map((c) => {\n const young = c.age_blocks < MIN_CONFIRMATIONS;\n return (\n <label\n key={c.outpoint}\n style={coinStyle(chosen === c.outpoint, young)}\n >\n <input\n type=\"radio\"\n name=\"oc-bond-coin\"\n value={c.outpoint}\n checked={chosen === c.outpoint}\n disabled={young}\n onChange={() => setChosen(c.outpoint)}\n style={{ accentColor: V.primary, margin: '2px 0 0' }}\n />\n <span style={{ display: 'grid', gap: 2, minWidth: 0 }}>\n <span\n style={{\n color: young ? V.muted : V.foreground,\n fontSize: 13,\n }}\n >\n {n(c.sats)} sats, held{' '}\n {Math.floor(c.age_blocks / 144)} days\n </span>\n <span style={smallStyle}>\n {young\n ? 'too recent: needs six confirmations'\n : `apps would see ${bondFloorsText(c)}`}\n </span>\n <span\n style={{\n ...smallStyle,\n fontFamily: SIGNIN_FONT,\n wordBreak: 'break-all',\n }}\n >\n {short(c.outpoint)}\n </span>\n </span>\n </label>\n );\n })}\n </div>\n {dust > 0 ? (\n <p style={{ ...smallStyle, marginTop: 8 }}>{dustText(dust)}</p>\n ) : null}\n </>\n )}\n {error}\n <FormButtons>\n {eligible.length > 0 ? (\n <button\n type=\"button\"\n style={primaryBtnStyle(busy || !chosen)}\n disabled={busy || !chosen}\n onClick={() => void getMessage()}\n >\n continue\n </button>\n ) : null}\n {backButton}\n {cancelButton}\n </FormButtons>\n </>\n );\n } else {\n step = (\n <form\n style={{ margin: 0 }}\n onSubmit={(e) => {\n e.preventDefault();\n if (canFind && !busy) void findCoins();\n }}\n >\n <label htmlFor={`${id}-address`} style={fieldLabelStyle}>\n bitcoin address that holds the coin\n </label>\n <p id={`${id}-advice`} style={{ ...bodyStyle, margin: '0 0 8px' }}>\n use one that holds nothing else of yours: orangecheck learns its address and\n amount. apps never do.\n </p>\n <input\n data-oca-field=\"\"\n id={`${id}-address`}\n aria-describedby={`${id}-advice`}\n style={inputStyle}\n value={address}\n onChange={(e) => setAddress(e.target.value)}\n placeholder=\"bc1q…\"\n spellCheck={false}\n autoComplete=\"off\"\n autoCapitalize=\"off\"\n />\n {error}\n <FormButtons>\n <button\n type=\"submit\"\n style={primaryBtnStyle(busy || !canFind)}\n disabled={busy || !canFind}\n >\n find its coins\n </button>\n {cancelButton}\n </FormButtons>\n </form>\n );\n }\n\n return (\n <div style={panelStyle('accent')} data-oc-bond-form=\"\">\n <SectionLabel>§ back your account</SectionLabel>\n {step}\n </div>\n );\n}\n"],"mappings":"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","names":[]}