@orangecheck/auth-client 2.37.0 → 2.37.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bond.mjs +4 -4
- package/dist/{chunk-EFMTFRCS.mjs → chunk-4YHFT5SR.mjs} +2 -2
- package/dist/{chunk-XCZVSE7M.mjs → chunk-AHGVGINO.mjs} +2 -2
- package/dist/{chunk-BMMNHG74.mjs → chunk-BSZCD5YA.mjs} +2 -2
- package/dist/{chunk-E2XQ7YED.mjs → chunk-DRRU6M5Z.mjs} +12 -4
- package/dist/chunk-DRRU6M5Z.mjs.map +1 -0
- package/dist/{chunk-VOO5KQQW.mjs → chunk-J32TKDC5.mjs} +2 -2
- package/dist/{chunk-EZKAMTG7.mjs → chunk-KNUHQUTI.mjs} +2 -2
- package/dist/{chunk-ASL3DS3Q.mjs → chunk-YUJNANKB.mjs} +3 -3
- package/dist/{chunk-VRLFZAGK.mjs → chunk-Z3HVBT5Q.mjs} +1 -1
- package/dist/chunk-Z3HVBT5Q.mjs.map +1 -0
- package/dist/components.mjs +3 -3
- package/dist/index.js +10 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +8 -8
- package/dist/linked-identities.mjs +3 -3
- package/dist/provider.mjs +2 -2
- package/dist/signin.mjs +4 -4
- package/dist/sudo.mjs +2 -2
- package/dist/types.mjs +1 -1
- package/dist/webauthn.mjs +3 -3
- package/package.json +1 -1
- package/dist/chunk-E2XQ7YED.mjs.map +0 -1
- package/dist/chunk-VRLFZAGK.mjs.map +0 -1
- /package/dist/{chunk-EFMTFRCS.mjs.map → chunk-4YHFT5SR.mjs.map} +0 -0
- /package/dist/{chunk-XCZVSE7M.mjs.map → chunk-AHGVGINO.mjs.map} +0 -0
- /package/dist/{chunk-BMMNHG74.mjs.map → chunk-BSZCD5YA.mjs.map} +0 -0
- /package/dist/{chunk-VOO5KQQW.mjs.map → chunk-J32TKDC5.mjs.map} +0 -0
- /package/dist/{chunk-EZKAMTG7.mjs.map → chunk-KNUHQUTI.mjs.map} +0 -0
- /package/dist/{chunk-ASL3DS3Q.mjs.map → chunk-YUJNANKB.mjs.map} +0 -0
package/dist/bond.mjs
CHANGED
|
@@ -4,12 +4,12 @@ import {
|
|
|
4
4
|
bondFloorsText,
|
|
5
5
|
bondRefusal,
|
|
6
6
|
fetchOcBond
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-4YHFT5SR.mjs";
|
|
8
|
+
import "./chunk-BSZCD5YA.mjs";
|
|
9
|
+
import "./chunk-AHGVGINO.mjs";
|
|
10
10
|
import "./chunk-U7IUGDLW.mjs";
|
|
11
11
|
import "./chunk-H2TXOCXH.mjs";
|
|
12
|
-
import "./chunk-
|
|
12
|
+
import "./chunk-Z3HVBT5Q.mjs";
|
|
13
13
|
import "./chunk-CYR7J4UY.mjs";
|
|
14
14
|
import "./chunk-TJJNTITX.mjs";
|
|
15
15
|
export {
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
inputStyle,
|
|
12
12
|
panelStyle,
|
|
13
13
|
primaryBtnStyle
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-BSZCD5YA.mjs";
|
|
15
15
|
import {
|
|
16
16
|
addressInputValue
|
|
17
17
|
} from "./chunk-U7IUGDLW.mjs";
|
|
@@ -491,4 +491,4 @@ export {
|
|
|
491
491
|
fetchOcBond,
|
|
492
492
|
OcBond
|
|
493
493
|
};
|
|
494
|
-
//# sourceMappingURL=chunk-
|
|
494
|
+
//# sourceMappingURL=chunk-4YHFT5SR.mjs.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
resolveConfig
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-Z3HVBT5Q.mjs";
|
|
4
4
|
import {
|
|
5
5
|
withoutFragment
|
|
6
6
|
} from "./chunk-CYR7J4UY.mjs";
|
|
@@ -37,4 +37,4 @@ export {
|
|
|
37
37
|
SUDO_ACCOUNT_MISMATCH_MESSAGE,
|
|
38
38
|
isSudoAccountMismatch
|
|
39
39
|
};
|
|
40
|
-
//# sourceMappingURL=chunk-
|
|
40
|
+
//# sourceMappingURL=chunk-AHGVGINO.mjs.map
|
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
SUDO_ACCOUNT_MISMATCH_MESSAGE,
|
|
3
3
|
handleSudoRequired,
|
|
4
4
|
isSudoAccountMismatch
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-AHGVGINO.mjs";
|
|
6
6
|
import {
|
|
7
7
|
addressInputValue
|
|
8
8
|
} from "./chunk-U7IUGDLW.mjs";
|
|
@@ -916,4 +916,4 @@ export {
|
|
|
916
916
|
ghostBtnStyle,
|
|
917
917
|
dangerBtnStyle
|
|
918
918
|
};
|
|
919
|
-
//# sourceMappingURL=chunk-
|
|
919
|
+
//# sourceMappingURL=chunk-BSZCD5YA.mjs.map
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
buildAddAccountUrl,
|
|
4
4
|
buildSignInUrl,
|
|
5
5
|
resolveConfig
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-Z3HVBT5Q.mjs";
|
|
7
7
|
import {
|
|
8
8
|
keepFragmentOnReturnLinks,
|
|
9
9
|
restoreReturnFragment
|
|
@@ -23,6 +23,12 @@ import {
|
|
|
23
23
|
import * as React from "react";
|
|
24
24
|
import { jsx } from "react/jsx-runtime";
|
|
25
25
|
var SessionContext = React.createContext(null);
|
|
26
|
+
function onHistory(change) {
|
|
27
|
+
window.addEventListener("popstate", change);
|
|
28
|
+
return () => window.removeEventListener("popstate", change);
|
|
29
|
+
}
|
|
30
|
+
var pageAddress = () => window.location.href.split("#")[0];
|
|
31
|
+
var noPageYet = () => void 0;
|
|
26
32
|
function normalizeDisplayIdentity(raw, didOc) {
|
|
27
33
|
const di = raw.display_identity ?? raw.displayIdentity;
|
|
28
34
|
if (di && typeof di === "object" && typeof di.value === "string" && di.value.length > 0 && typeof di.kind === "string" && DISPLAY_IDENTITY_KINDS.includes(di.kind)) {
|
|
@@ -310,8 +316,9 @@ function OcSessionProvider({
|
|
|
310
316
|
},
|
|
311
317
|
[cfg.authOrigin, refresh]
|
|
312
318
|
);
|
|
319
|
+
const here = React.useSyncExternalStore(onHistory, pageAddress, noPageYet);
|
|
313
320
|
const value = React.useMemo(() => {
|
|
314
|
-
const returnTo = defaultReturnTo ??
|
|
321
|
+
const returnTo = defaultReturnTo ?? here;
|
|
315
322
|
return {
|
|
316
323
|
status,
|
|
317
324
|
account,
|
|
@@ -337,7 +344,8 @@ function OcSessionProvider({
|
|
|
337
344
|
addAccountUrl,
|
|
338
345
|
setDisplayIdentity,
|
|
339
346
|
cfg,
|
|
340
|
-
defaultReturnTo
|
|
347
|
+
defaultReturnTo,
|
|
348
|
+
here
|
|
341
349
|
]);
|
|
342
350
|
return /* @__PURE__ */ jsx(SessionContext.Provider, { value, children });
|
|
343
351
|
}
|
|
@@ -360,4 +368,4 @@ export {
|
|
|
360
368
|
useOcSession,
|
|
361
369
|
useOptionalOcSession
|
|
362
370
|
};
|
|
363
|
-
//# sourceMappingURL=chunk-
|
|
371
|
+
//# sourceMappingURL=chunk-DRRU6M5Z.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/provider.tsx"],"sourcesContent":["import * as React from 'react';\n\nimport {\n buildAddAccountUrl,\n buildSignInUrl,\n DEFAULT_CONFIG,\n DISPLAY_IDENTITY_KINDS,\n resolveConfig,\n type DisplayIdentity,\n type DisplayIdentityKind,\n type OcAccount,\n type OcAccountSummary,\n type OcAuthConfig,\n type OcSessionState,\n type OcSignOutScope,\n} from './types';\nimport { keepFragmentOnReturnLinks, restoreReturnFragment } from './return-to';\nimport {\n clearTabSession,\n consumeTabAccountHint,\n consumeTabAdoptMarker,\n installTabFetchInterceptor,\n installTabLinkDecorator,\n readTabSession,\n tabSessionHeader,\n writeTabSession,\n} from './tab-session';\n\nconst SessionContext = React.createContext<OcSessionState | null>(null);\n\n/** Back and forward; a router's move renders the tree again, which reads the address again. */\nfunction onHistory(change: () => void): () => void {\n window.addEventListener('popstate', change);\n return () => window.removeEventListener('popstate', change);\n}\nconst pageAddress = () => window.location.href.split('#')[0];\n/** The server has no page, and the browser's first render must say what the server's HTML said. */\nconst noPageYet = () => undefined;\n\ntype RawSigningMethod = 'fedimint_threshold' | 'fedimint_client' | 'bip322';\n\ninterface RawRosterEntry {\n did_oc?: string;\n didOc?: string;\n display_name?: string | null;\n displayName?: string | null;\n primary_btc?: string | null;\n primaryBtc?: string | null;\n display_identity?: { kind?: string; value?: string } | null;\n displayIdentity?: { kind?: string; value?: string } | null;\n last_seen_at?: string | null;\n lastSeenAt?: string | null;\n}\n\ninterface MeResponse {\n account?: {\n id?: string;\n account_id?: string;\n accountId?: string;\n did_oc?: string;\n didOc?: string;\n primary_btc?: string | null;\n primaryBtc?: string | null;\n has_email?: boolean;\n hasEmail?: boolean;\n display_name?: string | null;\n displayName?: string | null;\n nostr_npub?: string | null;\n nostrNpub?: string | null;\n home_federation_slug?: string | null;\n homeFederation?: string | null;\n signing_method?: RawSigningMethod | null;\n signingMethod?: RawSigningMethod | null;\n is_owner?: boolean;\n isOwner?: boolean;\n display_identity?: { kind?: string; value?: string } | null;\n displayIdentity?: { kind?: string; value?: string } | null;\n };\n /** Multi-account roster · other accounts in this browser. Absent\n * on hosts that haven't deployed the multi-account migration yet\n * — treated as `[]` (single-account fallback). */\n roster?: RawRosterEntry[];\n}\n\n/**\n * Resolve the badge identity from a `/api/auth/me` account payload.\n * Total: returns the carried `{kind,value}` when well-formed, else\n * `{kind:'did',value:didOc}` — so accounts that never promoted, and\n * sessions minted before the field shipped, degrade cleanly.\n */\nfunction normalizeDisplayIdentity(\n raw: NonNullable<MeResponse['account']>,\n didOc: string\n): DisplayIdentity {\n const di = raw.display_identity ?? raw.displayIdentity;\n if (\n di &&\n typeof di === 'object' &&\n typeof di.value === 'string' &&\n di.value.length > 0 &&\n typeof di.kind === 'string' &&\n (DISPLAY_IDENTITY_KINDS as readonly string[]).includes(di.kind)\n ) {\n return { kind: di.kind as DisplayIdentityKind, value: di.value };\n }\n return { kind: 'did', value: didOc };\n}\n\n/**\n * Multi-account · normalize one roster entry from /api/auth/me. Drops\n * entries with no did_oc (defensive — the host shouldn't emit those).\n */\nfunction normalizeRosterEntry(raw: RawRosterEntry): OcAccountSummary | null {\n const didOc = raw.did_oc ?? raw.didOc;\n if (!didOc) return null;\n const di = raw.display_identity ?? raw.displayIdentity;\n const displayIdentity: DisplayIdentity =\n di &&\n typeof di === 'object' &&\n typeof di.value === 'string' &&\n di.value.length > 0 &&\n typeof di.kind === 'string' &&\n (DISPLAY_IDENTITY_KINDS as readonly string[]).includes(di.kind)\n ? { kind: di.kind as DisplayIdentityKind, value: di.value }\n : { kind: 'did', value: didOc };\n return {\n didOc,\n displayName: raw.display_name ?? raw.displayName ?? null,\n primaryBtc: raw.primary_btc ?? raw.primaryBtc ?? null,\n displayIdentity,\n lastSeenAt: raw.last_seen_at ?? raw.lastSeenAt ?? null,\n };\n}\n\n/**\n * Multi-account · fetch the roster from the AUTH HOST cross-origin.\n *\n * Consumer subdomains expose a LOCAL `/api/auth/me` that verifies the JWT\n * but has no database, so it never carries the `roster` (which is host DB\n * state). When the local /me comes back authenticated with an empty roster,\n * we source it from the host directly — the same credentialed cross-origin\n * pattern this provider already uses for logout + account PATCH, and the\n * host already serves `.ochk.io` consumers via CORS.\n *\n * Best-effort by construction: any failure resolves to `[]`, so a roster\n * fetch can never change the authenticated state derived from the local /me.\n */\n/** How long a session check may take before the page stops waiting on it. */\nexport const ME_TIMEOUT_MS = 10_000;\n\n/** `AbortSignal.timeout` where the browser has it, a timer where it does not. */\nfunction timeoutSignal(ms: number): AbortSignal {\n if (typeof AbortSignal.timeout === 'function') return AbortSignal.timeout(ms);\n const controller = new AbortController();\n setTimeout(() => controller.abort(), ms);\n return controller.signal;\n}\n\nfunction isTimeout(err: unknown): boolean {\n const name = (err as { name?: unknown } | null)?.name;\n return name === 'TimeoutError' || name === 'AbortError';\n}\n\nasync function fetchHostRoster(cfg: Required<OcAuthConfig>): Promise<OcAccountSummary[]> {\n try {\n // Per-tab · carry the pin so the host computes \"peers\" relative\n // to THIS tab's effective account, not the cookie default.\n const res = await fetch(`${cfg.authOrigin}${cfg.mePath}`, {\n method: 'GET',\n credentials: 'include',\n headers: { Accept: 'application/json', ...tabSessionHeader() },\n signal: timeoutSignal(ME_TIMEOUT_MS),\n });\n if (!res.ok) return [];\n const body = (await res.json()) as MeResponse;\n return Array.isArray(body.roster)\n ? body.roster\n .map(normalizeRosterEntry)\n .filter((r): r is OcAccountSummary => r !== null)\n : [];\n } catch {\n return [];\n }\n}\n\nfunction normalizeAccount(raw: MeResponse['account']): OcAccount | null {\n if (!raw) return null;\n const didOc = raw.did_oc ?? raw.didOc;\n const accountId = raw.id ?? raw.account_id ?? raw.accountId;\n if (!didOc || !accountId) return null;\n return {\n accountId,\n didOc,\n primaryBtc: raw.primary_btc ?? raw.primaryBtc ?? null,\n hasEmail: raw.has_email ?? raw.hasEmail ?? false,\n displayName: raw.display_name ?? raw.displayName ?? null,\n nostrNpub: raw.nostr_npub ?? raw.nostrNpub ?? null,\n homeFederation: raw.home_federation_slug ?? raw.homeFederation ?? null,\n signingMethod: raw.signing_method ?? raw.signingMethod ?? null,\n isOwner: Boolean(raw.is_owner ?? raw.isOwner ?? false),\n displayIdentity: normalizeDisplayIdentity(raw, didOc),\n };\n}\n\nexport interface OcSessionProviderProps {\n children: React.ReactNode;\n config?: OcAuthConfig;\n /**\n * Optional return URL passed to the sign-in page. Defaults to the\n * current page. Its fragment is never sent: it is kept in this tab and\n * restored on return (./return-to).\n */\n defaultReturnTo?: string;\n}\n\n/**\n * Top-level provider that exposes the cross-subdomain oc_session to every\n * component below it. Mount once, near the root of your tree.\n */\nexport function OcSessionProvider({\n children,\n config,\n defaultReturnTo,\n}: OcSessionProviderProps): React.ReactElement {\n const cfg = React.useMemo(() => resolveConfig(config), [config]);\n // Back from a sign-in or a check: put back the fragment its return\n // address left in this tab, before any page below reads the address.\n // The adoption marker the auth host appends goes first, so the fragment\n // takes its place. Children's effects run before this provider's, so\n // this happens during the first render, once; both calls are idempotent.\n React.useState(() => {\n if (typeof window !== 'undefined') {\n consumeTabAdoptMarker();\n restoreReturnFragment();\n }\n return null;\n });\n const [account, setAccount] = React.useState<OcAccount | null>(null);\n const [roster, setRoster] = React.useState<OcAccountSummary[]>([]);\n const [status, setStatus] = React.useState<OcSessionState['status']>('loading');\n const [error, setError] = React.useState<Error | null>(null);\n const [tabPinned, setTabPinned] = React.useState(false);\n\n // Per-tab · mint a pin for this tab via the host. Called once per\n // tab on first authenticated resolve, so a later switch in ANOTHER\n // tab can't yank this one — it keeps presenting its own token.\n // Best-effort: a host that predates `/api/auth/tab` (or a network\n // failure) leaves the tab unpinned, which is exactly the legacy\n // cookie-following behavior.\n const pinInFlightRef = React.useRef(false);\n const pinThisTab = React.useCallback(\n async (didOc: string) => {\n if (!cfg.multiAccount || pinInFlightRef.current || readTabSession()) return;\n pinInFlightRef.current = true;\n try {\n const res = await fetch(`${cfg.authOrigin}/api/auth/tab`, {\n method: 'POST',\n credentials: 'include',\n headers: { Accept: 'application/json' },\n });\n if (!res.ok) return;\n const body = (await res.json()) as {\n ok?: boolean;\n token?: string;\n account?: { did_oc?: string };\n };\n // Only pin when the host minted for the account this tab\n // just resolved — a cookie flip racing between our /me and\n // /tab calls must not pin the WRONG account.\n if (\n body.ok &&\n typeof body.token === 'string' &&\n body.account?.did_oc === didOc\n ) {\n writeTabSession({ token: body.token, didOc });\n setTabPinned(true);\n }\n } catch {\n // best-effort by construction\n } finally {\n pinInFlightRef.current = false;\n }\n },\n [cfg.authOrigin, cfg.multiAccount]\n );\n\n const refresh = React.useCallback(async () => {\n if (typeof window === 'undefined') return;\n try {\n let pin = readTabSession();\n // A check that never answers would leave the page loading for good;\n // past the timeout it is an error, which every site already shows.\n let res = await fetch(cfg.mePath, {\n method: 'GET',\n credentials: 'include',\n headers: { Accept: 'application/json', ...tabSessionHeader() },\n signal: timeoutSignal(ME_TIMEOUT_MS),\n });\n // Per-tab · 401 with a pin present means the pinned token is\n // dead (expired, or revoked and rejected by the host). Drop\n // the pin and retry once as the browser-default account.\n if (res.status === 401 && pin) {\n clearTabSession();\n pin = null;\n setTabPinned(false);\n res = await fetch(cfg.mePath, {\n method: 'GET',\n credentials: 'include',\n headers: { Accept: 'application/json' },\n signal: timeoutSignal(ME_TIMEOUT_MS),\n });\n }\n if (res.status === 401) {\n setAccount(null);\n setRoster([]);\n setStatus('anonymous');\n setError(null);\n return;\n }\n if (!res.ok) {\n setStatus('error');\n setError(new Error(`me endpoint returned ${res.status}`));\n return;\n }\n const body = (await res.json()) as MeResponse;\n const acct = normalizeAccount(body.account);\n // Per-tab reconciliation · the server answered as a different\n // account than the pin → it doesn't honor `x-oc-tab-session`\n // yet (pre-migration deploy). Drop the pin: the UI must never\n // display an account the server isn't acting as.\n if (acct && pin && acct.didOc !== pin.didOc) {\n clearTabSession();\n pin = null;\n }\n setTabPinned(pin !== null);\n const rosterEntries = Array.isArray(body.roster)\n ? body.roster\n .map(normalizeRosterEntry)\n .filter((r): r is OcAccountSummary => r !== null)\n : [];\n setAccount(acct);\n setRoster(rosterEntries);\n setStatus(acct ? 'authenticated' : 'anonymous');\n setError(null);\n\n // Per-tab · authenticated but unpinned → pin this tab to the\n // account it just resolved (fire-and-forget).\n if (acct && !pin) void pinThisTab(acct.didOc);\n\n // Multi-account · the local /me carried no roster (the standard\n // case on consumer subdomains, whose local /me has no DB). When\n // we're authenticated and NOT already on the auth host, source\n // the roster from the host so the §accounts switcher lights up.\n // Best-effort: never touches account/status on failure.\n const currentOrigin =\n typeof window !== 'undefined' ? window.location.origin : null;\n if (\n cfg.multiAccount &&\n acct &&\n rosterEntries.length === 0 &&\n currentOrigin !== null &&\n !cfg.authOrigin.startsWith(currentOrigin)\n ) {\n const peers = await fetchHostRoster(cfg);\n if (peers.length > 0) setRoster(peers);\n }\n } catch (err) {\n setStatus('error');\n setError(\n isTimeout(err)\n ? new Error(`me endpoint did not answer within ${ME_TIMEOUT_MS / 1000}s`)\n : err instanceof Error\n ? err\n : new Error(String(err))\n );\n }\n }, [cfg, pinThisTab]);\n\n React.useEffect(() => {\n let cancelled = false;\n // Post-ceremony adoption · the auth host appends `#oc-adopt` when\n // returning from a sign-in/add ceremony: the user expects THIS tab\n // to become the account they just authenticated, so a stale pin\n // must not survive the round trip.\n consumeTabAdoptMarker();\n void (async () => {\n // New-tab inheritance · if the opener stamped `#oc-as=<did>`,\n // adopt that account BEFORE the first /me fetch so this tab\n // resolves as the opener's account, not the cookie default.\n if (cfg.multiAccount) await consumeTabAccountHint(cfg.authOrigin);\n if (!cancelled) void refresh();\n })();\n return () => {\n cancelled = true;\n };\n }, [refresh, cfg.authOrigin, cfg.multiAccount]);\n\n // A link that brings the browser back here keeps this page's fragment\n // in the tab, since its return_to cannot carry it.\n React.useEffect(() => keepFragmentOnReturnLinks(), []);\n\n // Per-tab · attach the pin to every same-site fetch so app-level data\n // calls execute as the account this tab displays. Scoped + reversible;\n // see installTabFetchInterceptor for the conservative rules.\n React.useEffect(\n () => (cfg.multiAccount ? installTabFetchInterceptor(cfg.authOrigin) : undefined),\n [cfg.authOrigin, cfg.multiAccount]\n );\n\n // Per-tab · stamp this tab's effective account onto outgoing\n // family-origin links so a new/cross-subdomain tab inherits it\n // instead of the shared cookie default. See installTabLinkDecorator.\n React.useEffect(\n () => (cfg.multiAccount ? installTabLinkDecorator(cfg.authOrigin) : undefined),\n [cfg.authOrigin, cfg.multiAccount]\n );\n\n const signOut = React.useCallback(\n async (opts?: { scope?: OcSignOutScope }) => {\n const scope: OcSignOutScope = opts?.scope ?? 'all';\n try {\n const url = new URL(`${cfg.authOrigin}${cfg.logoutPath}`);\n if (scope === 'current') url.searchParams.set('scope', 'current');\n const res = await fetch(url.toString(), {\n method: 'POST',\n credentials: 'include',\n // Per-tab · carry the pin so scope='current' signs out\n // the account THIS tab is operating as, not whichever\n // account the shared cookie happens to point at.\n headers: { ...tabSessionHeader() },\n // `keepalive` lets the logout round-trip complete even if\n // the caller hard-navigates away in the same tick (e.g.\n // `<OcAccountMenu>` redirects home immediately on sign-out).\n // Without it the in-flight request is cancelled on unload\n // and the `.ochk.io` cookie may never get cleared.\n keepalive: true,\n });\n // The signed-out account's token is dead either way — the\n // pin must not outlive it.\n clearTabSession();\n setTabPinned(false);\n // Multi-account: with scope='current', the server may have\n // switched the active session to a roster peer instead of\n // clearing the cookie. Re-fetch /api/auth/me so this\n // provider reflects the new active account; if the body\n // says `switched_to !== null`, refresh resolves to the\n // peer, otherwise it resolves to anonymous and the\n // setState below is redundant-but-harmless.\n if (scope === 'current' && res.ok) {\n await refresh();\n return;\n }\n } catch {\n // fall through — we still clear local state so the UI reflects\n // the user's intent even if the server round-trip fails.\n clearTabSession();\n setTabPinned(false);\n }\n setAccount(null);\n setRoster([]);\n setStatus('anonymous');\n },\n [cfg.authOrigin, cfg.logoutPath, refresh]\n );\n\n const switchAccount = React.useCallback(\n async (didOc: string) => {\n if (typeof window === 'undefined') return;\n const res = await fetch(`${cfg.authOrigin}/api/auth/switch`, {\n method: 'POST',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json', ...tabSessionHeader() },\n body: JSON.stringify({ did_oc: didOc }),\n });\n if (!res.ok) {\n let reason = `http_${res.status}`;\n try {\n const body = (await res.json()) as { reason?: string };\n if (body.reason) reason = body.reason;\n } catch {\n // keep http_ fallback\n }\n throw new Error(`[@orangecheck/auth-client] switchAccount failed: ${reason}`);\n }\n // Per-tab · pin THIS tab to the switch target using the fresh\n // JWT the host echoes in the body. The host has also flipped\n // the shared cookie — the default for FUTURE tabs — but other\n // open tabs keep their own pins and are not yanked. Pin to the\n // did the SERVER minted for (merge-graph collapse may differ\n // from the requested did).\n try {\n const body = (await res.json()) as {\n token?: string;\n account?: { did_oc?: string };\n };\n if (typeof body.token === 'string' && body.account?.did_oc) {\n writeTabSession({ token: body.token, didOc: body.account.did_oc });\n setTabPinned(true);\n }\n } catch {\n // body unreadable — cookie still flipped; refresh resolves it\n }\n // Re-fetch /me to surface the new active account + freshly\n // computed roster (the previous active account is now a peer,\n // swap is symmetric).\n await refresh();\n },\n [cfg.authOrigin, refresh]\n );\n\n const addAccountUrl = React.useCallback(\n (returnTo?: string) => {\n const rt =\n returnTo ?? (typeof window !== 'undefined' ? window.location.href : undefined);\n return buildAddAccountUrl(cfg, rt);\n },\n [cfg]\n );\n\n const setDisplayIdentity = React.useCallback(\n async (kind: DisplayIdentityKind) => {\n if (typeof window === 'undefined') return;\n // PATCH the auth host directly (family-CORS). It writes the\n // pref, re-mints the `.ochk.io` session cookie with the new\n // `display_identity` claim, and Set-Cookies it back — so the\n // choice propagates to every subdomain. Then refresh so this\n // session reflects it immediately.\n const res = await fetch(`${cfg.authOrigin}/api/auth/account`, {\n method: 'PATCH',\n credentials: 'include',\n headers: { 'Content-Type': 'application/json', ...tabSessionHeader() },\n body: JSON.stringify({ display_identity: kind }),\n });\n if (!res.ok) {\n let reason = `http_${res.status}`;\n try {\n const body = (await res.json()) as { reason?: string };\n if (body.reason) reason = body.reason;\n } catch {\n // keep the http_ fallback\n }\n throw new Error(`[@orangecheck/auth-client] setDisplayIdentity failed: ${reason}`);\n }\n // Per-tab · the PATCH re-mints; when this tab is pinned the\n // host echoes the fresh token in the body (it only rewrites\n // the shared cookie when the cookie account was the actor).\n // Re-pin so the tab carries the updated claims.\n try {\n const body = (await res.json()) as {\n token?: string;\n account?: { did_oc?: string };\n };\n const pin = readTabSession();\n if (\n pin &&\n typeof body.token === 'string' &&\n body.account?.did_oc === pin.didOc\n ) {\n writeTabSession({ token: body.token, didOc: pin.didOc });\n }\n } catch {\n // body unreadable — refresh below still reconciles state\n }\n await refresh();\n },\n [cfg.authOrigin, refresh]\n );\n\n // Read through the store, not from the window as this renders: a server-rendered\n // link keeps the server's href when hydrating, so the page is added after.\n const here = React.useSyncExternalStore(onHistory, pageAddress, noPageYet);\n const value = React.useMemo<OcSessionState>(() => {\n const returnTo = defaultReturnTo ?? here;\n return {\n status,\n account,\n roster,\n tabPinned,\n error,\n refresh,\n signOut,\n switchAccount,\n addAccountUrl,\n setDisplayIdentity,\n signInUrl: buildSignInUrl(cfg, returnTo),\n };\n }, [\n status,\n account,\n roster,\n tabPinned,\n error,\n refresh,\n signOut,\n switchAccount,\n addAccountUrl,\n setDisplayIdentity,\n cfg,\n defaultReturnTo,\n here,\n ]);\n\n return <SessionContext.Provider value={value}>{children}</SessionContext.Provider>;\n}\n\n/**\n * Access the current cross-subdomain oc_session. Must be called inside\n * an `<OcSessionProvider>`.\n */\nexport function useOcSession(): OcSessionState {\n const ctx = React.useContext(SessionContext);\n if (!ctx) {\n throw new Error(\n '[@orangecheck/auth-client] useOcSession() must be called inside <OcSessionProvider>'\n );\n }\n return ctx;\n}\n\n/**\n * Non-throwing variant — returns `null` if called outside a provider.\n * Useful for libraries that want to read the session *if it exists* but\n * shouldn't crash on apps that haven't opted in.\n */\nexport function useOptionalOcSession(): OcSessionState | null {\n return React.useContext(SessionContext);\n}\n\nexport { DEFAULT_CONFIG };\n"],"mappings":"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","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
LinkPromptStep
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-BSZCD5YA.mjs";
|
|
4
4
|
import {
|
|
5
5
|
addressInputValue
|
|
6
6
|
} from "./chunk-U7IUGDLW.mjs";
|
|
@@ -1208,4 +1208,4 @@ export {
|
|
|
1208
1208
|
looksLikeBitcoinAddress,
|
|
1209
1209
|
OcSignIn
|
|
1210
1210
|
};
|
|
1211
|
-
//# sourceMappingURL=chunk-
|
|
1211
|
+
//# sourceMappingURL=chunk-J32TKDC5.mjs.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
useOcSession
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-DRRU6M5Z.mjs";
|
|
4
4
|
|
|
5
5
|
// src/components.tsx
|
|
6
6
|
import * as React from "react";
|
|
@@ -649,4 +649,4 @@ export {
|
|
|
649
649
|
useOcAddressSuggestion,
|
|
650
650
|
OcAddressInput
|
|
651
651
|
};
|
|
652
|
-
//# sourceMappingURL=chunk-
|
|
652
|
+
//# sourceMappingURL=chunk-KNUHQUTI.mjs.map
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
useOcSession
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-DRRU6M5Z.mjs";
|
|
4
4
|
import {
|
|
5
5
|
resolveConfig
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-Z3HVBT5Q.mjs";
|
|
7
7
|
|
|
8
8
|
// src/webauthn.tsx
|
|
9
9
|
import * as React from "react";
|
|
@@ -250,4 +250,4 @@ export {
|
|
|
250
250
|
useWebAuthnList,
|
|
251
251
|
useStepUpAuth
|
|
252
252
|
};
|
|
253
|
-
//# sourceMappingURL=chunk-
|
|
253
|
+
//# sourceMappingURL=chunk-YUJNANKB.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/types.ts"],"sourcesContent":["import { stripFragment } from './return-to';\n\n/**\n * The kinds of identity a user can surface as their account-badge\n * label. `did` is the canonical `did:oc` identifier — always available\n * and the ultimate fallback; the other three are linked identities a\n * user may or may not have.\n */\nexport const DISPLAY_IDENTITY_KINDS = ['did', 'btc', 'email', 'nostr'] as const;\nexport type DisplayIdentityKind = (typeof DISPLAY_IDENTITY_KINDS)[number];\n\n/**\n * The identity a user has chosen to show in their account badge — the\n * collapsed label every family site renders instead of the raw\n * `did:oc`. Carried as a JWT claim by the auth host, so the choice is\n * consistent across every `.ochk.io` subdomain with no round-trip.\n *\n * `kind` is which identity; `value` is the full, renderable value (the\n * `did:oc`, the Bitcoin address, the email, or the npub). Integrators\n * rendering their own chip read `useOcSession().account.displayIdentity`\n * — it is always populated (it defaults to the `did`).\n */\nexport interface DisplayIdentity {\n kind: DisplayIdentityKind;\n value: string;\n}\n\nexport interface OcAccount {\n accountId: string;\n /**\n * Opaque public-facing identifier · `did:oc:<32-hex>`. The sole\n * user identifier post auth-refactor. Stable across linking\n * events. Per AUTH-REFACTOR-PLAN.md §2.1.\n */\n didOc: string;\n /**\n * Primary linked Bitcoin address, plaintext, when the user has\n * one — null for email-OTP users until they link a btc address.\n * Surfaced inline by the auth host's /api/auth/me so consumer\n * dashboards can render the user's own footprint without a\n * second round-trip through /api/auth/identities.\n */\n primaryBtc?: string | null;\n /**\n * True when the user has a primary email linked. Plaintext is\n * fetchable on demand via /api/auth/identities; not surfaced\n * here.\n */\n hasEmail?: boolean;\n displayName?: string | null;\n nostrNpub?: string | null;\n /**\n * @deprecated Always null: the auth host no longer sets it. Where a\n * person's sats sit is me.ochk.io's to say (`/api/me/balance`).\n */\n homeFederation?: string | null;\n /**\n * Whether the account signs with its own Bitcoin key, derived by the\n * auth host from its verified linked identities; no one can set it.\n * 'bip322' when a BIP-322-verified address is linked, otherwise\n * 'fedimint_threshold'. 'fedimint_client' is reserved and never set.\n * It says who holds the signing key, never who holds the sats.\n */\n signingMethod?: 'fedimint_threshold' | 'fedimint_client' | 'bip322' | null;\n /**\n * Best-effort owner-flag · true when the user's `did_oc` was on\n * the auth host's `OWNER_OC_ADDRESSES` env at the time the JWT\n * was minted. Surfaced so the family-switcher and other low-\n * stakes UX can render owner-only affordances (e.g. an\n * analytics.ochk.io entry visible only to owners).\n *\n * **NOT A SECURITY BOUNDARY.** Sensitive surfaces — including\n * analytics.ochk.io itself — re-check the live env against\n * `session.did_oc` server-side on every request. If an owner is\n * removed from the env, their JWT may keep `isOwner: true` for\n * up to the JWT lifetime (~30d) but every gated action they\n * attempt re-fails. The flag exists purely so we can show or\n * hide UX hints without an extra round-trip on every page load.\n *\n * Treat absence as `false`.\n */\n isOwner?: boolean;\n /**\n * The identity the user has chosen to show as their account-badge\n * label — `{ kind, value }`. **Always populated**: when the user\n * has never promoted an identity (and on sessions minted before\n * the feature shipped) this is `{ kind:'did', value:didOc }`.\n *\n * `<OcAccountMenu>` renders `value` (shortened) as the collapsed\n * badge label. Integrators rendering their own chip read this\n * directly; change it with `useOcSession().setDisplayIdentity()`.\n */\n displayIdentity: DisplayIdentity;\n}\n\nexport type OcSessionStatus = 'loading' | 'authenticated' | 'anonymous' | 'error';\n\n/**\n * Multi-account roster · summary of another account this browser has\n * signed into and can switch to without re-authenticating. Surfaced by\n * `<OcAccountMenu>` in the `§ accounts` section. Returned by the auth\n * host's `/api/auth/me` as `roster: [...]` (an empty array on single-\n * account or pre-multi-account sessions).\n *\n * Sessions for peer accounts stay alive in the auth host's DB for the\n * normal 30-day window; once expired, the peer falls out of the roster\n * and the user must re-authenticate to bring it back in.\n */\nexport interface OcAccountSummary {\n didOc: string;\n displayName: string | null;\n primaryBtc: string | null;\n displayIdentity: DisplayIdentity;\n /** ISO timestamp of the last verified request on this account's\n * session row, or null if never touched. */\n lastSeenAt: string | null;\n}\n\n/**\n * Scope hint for `signOut()` in a multi-account browser.\n *\n * - 'all' · sign out of every account in this browser's roster\n * (the default; matches the historical single-account\n * behavior of \"clear the cookie\").\n * - 'current' · sign out of just the active account; if the roster\n * has other accounts, the auth host hands you off to\n * the next-most-recent one without a re-auth.\n */\nexport type OcSignOutScope = 'all' | 'current';\n\nexport interface OcSessionState {\n status: OcSessionStatus;\n account: OcAccount | null;\n /**\n * Multi-account · other accounts the user has signed into in this\n * browser. `[]` for single-account sessions and for tokens minted\n * before multi-account shipped. The active account (`account`\n * above) is NOT included — these are the *switch targets*.\n */\n roster: OcAccountSummary[];\n /**\n * Per-tab pinning · `true` when this tab holds its own session\n * token (sessionStorage) and operates as `account` independently of\n * the shared `.ochk.io` cookie — i.e. switching accounts in another\n * tab will NOT change this tab's identity. `false` on hosts that\n * predate `/api/auth/tab`, in privacy modes without sessionStorage,\n * and while anonymous.\n */\n tabPinned: boolean;\n /** `null` while loading; an `Error` instance when `status === 'error'`. */\n error: Error | null;\n /** Re-fetch the session. Useful after sign-in/sign-out happens elsewhere. */\n refresh: () => Promise<void>;\n /**\n * Trigger a sign-out. By default signs out of EVERY account in the\n * roster (back-compat); pass `{ scope: 'current' }` to sign out of\n * just the active account and stay logged into the next peer.\n */\n signOut: (opts?: { scope?: OcSignOutScope }) => Promise<void>;\n /**\n * Multi-account · flip the active session to a different account\n * in the roster. Resolves once the new cookie has been set and\n * the session has been re-fetched. Throws if the target `did_oc`\n * isn't in the current roster (the user must add it via\n * {@link addAccount} first) or if the auth host is unreachable.\n */\n switchAccount: (didOc: string) => Promise<void>;\n /**\n * Multi-account · open the sign-in flow in \"add\" mode — the new\n * account joins the current browser's roster instead of replacing\n * it. Returns the URL to navigate to (so callers can mount it in\n * a popup or hard-navigate as they prefer); pass `returnTo` to\n * route back to a specific page after the add completes.\n */\n addAccountUrl: (returnTo?: string) => string;\n /**\n * Promote a linked identity to be the account-badge label across\n * every `.ochk.io` site. PATCHes the auth host, which re-mints the\n * session cookie with the new `display_identity` claim, then\n * `refresh()`es this session. Rejects if the chosen kind is not a\n * verified identity on the account (`btc` / `email` / `nostr` must\n * actually be linked; `did` is always valid).\n */\n setDisplayIdentity: (kind: DisplayIdentityKind) => Promise<void>;\n /** URL to navigate to for sign-in on the auth host. */\n signInUrl: string;\n}\n\nexport interface OcAuthConfig {\n /**\n * Origin of the auth host — the subdomain that runs the sign-in UI,\n * issues session cookies, and exposes `/api/auth/me` + `/api/auth/logout`.\n *\n * Defaults to `https://ochk.io`. Override in preview/dev.\n */\n authOrigin?: string;\n /**\n * Path on the auth host that accepts `?return_to=<url>` and drives the\n * sign-in flow. The page offers two paths in-place:\n *\n * - email + OTP (default — no wallet and no key of the user's own;\n * identity is `did:email:<sha256(email)>`)\n * - BIP-322 wallet sign (paste address → in-page wallet sign;\n * identity is the Bitcoin address itself)\n *\n * Defaults to `/signin`.\n */\n signInPath?: string;\n /**\n * Local path (same origin as the current app) that exposes the\n * crypto-verified session. If your app ships one at `/api/auth/me`,\n * leave as default. Returns 200 `{ account }` or 401.\n */\n mePath?: string;\n /**\n * Path on the auth host to hit to clear the session cookie.\n * Defaults to `/api/auth/logout`. Called with `credentials: 'include'`\n * so the `.ochk.io` cookie is sent along.\n */\n logoutPath?: string;\n /**\n * Multi-account and per-tab pinning against the auth host. Defaults to\n * `true`. A site that holds its own single session cookie (ochk.app)\n * sets `false`: no tab pins, no account hints or link stamping, and no\n * roster fetched from the host.\n */\n multiAccount?: boolean;\n}\n\nexport const DEFAULT_CONFIG: Required<OcAuthConfig> = {\n authOrigin: 'https://ochk.io',\n signInPath: '/signin',\n mePath: '/api/auth/me',\n logoutPath: '/api/auth/logout',\n multiAccount: true,\n};\n\nexport function resolveConfig(cfg: OcAuthConfig | undefined): Required<OcAuthConfig> {\n return { ...DEFAULT_CONFIG, ...(cfg ?? {}) };\n}\n\n/**\n * The sign-in URL on the auth host. `returnTo` goes without its fragment;\n * OcSessionProvider keeps the fragment when the link is followed.\n */\nexport function buildSignInUrl(cfg: Required<OcAuthConfig>, returnTo?: string): string {\n const base = `${cfg.authOrigin}${cfg.signInPath}`;\n if (!returnTo) return base;\n const u = new URL(base);\n u.searchParams.set('return_to', stripFragment(returnTo));\n return u.toString();\n}\n\n/**\n * Multi-account · build the URL for the \"add another account\" entry\n * point. Same as {@link buildSignInUrl} but appends `?add=1` so the\n * sign-in page knows to preserve the current roster (the new account\n * is appended; the previously-active account stays signed in too and\n * remains switch-target reachable).\n */\nexport function buildAddAccountUrl(cfg: Required<OcAuthConfig>, returnTo?: string): string {\n const u = new URL(buildSignInUrl(cfg, returnTo));\n u.searchParams.set('add', '1');\n return u.toString();\n}\n"],"mappings":";;;;;AAQO,IAAM,yBAAyB,CAAC,OAAO,OAAO,SAAS,OAAO;AA6N9D,IAAM,iBAAyC;AAAA,EAClD,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,cAAc;AAClB;AAEO,SAAS,cAAc,KAAuD;AACjF,SAAO,EAAE,GAAG,gBAAgB,GAAI,OAAO,CAAC,EAAG;AAC/C;AAMO,SAAS,eAAe,KAA6B,UAA2B;AACnF,QAAM,OAAO,GAAG,IAAI,UAAU,GAAG,IAAI,UAAU;AAC/C,MAAI,CAAC,SAAU,QAAO;AACtB,QAAM,IAAI,IAAI,IAAI,IAAI;AACtB,IAAE,aAAa,IAAI,aAAa,cAAc,QAAQ,CAAC;AACvD,SAAO,EAAE,SAAS;AACtB;AASO,SAAS,mBAAmB,KAA6B,UAA2B;AACvF,QAAM,IAAI,IAAI,IAAI,eAAe,KAAK,QAAQ,CAAC;AAC/C,IAAE,aAAa,IAAI,OAAO,GAAG;AAC7B,SAAO,EAAE,SAAS;AACtB;","names":[]}
|
package/dist/components.mjs
CHANGED
|
@@ -4,9 +4,9 @@ import {
|
|
|
4
4
|
OcAddressInput,
|
|
5
5
|
OcSignInButton,
|
|
6
6
|
useOcAddressSuggestion
|
|
7
|
-
} from "./chunk-
|
|
8
|
-
import "./chunk-
|
|
9
|
-
import "./chunk-
|
|
7
|
+
} from "./chunk-KNUHQUTI.mjs";
|
|
8
|
+
import "./chunk-DRRU6M5Z.mjs";
|
|
9
|
+
import "./chunk-Z3HVBT5Q.mjs";
|
|
10
10
|
import "./chunk-CYR7J4UY.mjs";
|
|
11
11
|
import "./chunk-TJJNTITX.mjs";
|
|
12
12
|
export {
|
package/dist/index.js
CHANGED
|
@@ -329,6 +329,12 @@ function buildAddAccountUrl(cfg, returnTo) {
|
|
|
329
329
|
return u.toString();
|
|
330
330
|
}
|
|
331
331
|
var SessionContext = React4__namespace.createContext(null);
|
|
332
|
+
function onHistory(change) {
|
|
333
|
+
window.addEventListener("popstate", change);
|
|
334
|
+
return () => window.removeEventListener("popstate", change);
|
|
335
|
+
}
|
|
336
|
+
var pageAddress = () => window.location.href.split("#")[0];
|
|
337
|
+
var noPageYet = () => void 0;
|
|
332
338
|
function normalizeDisplayIdentity(raw, didOc) {
|
|
333
339
|
const di = raw.display_identity ?? raw.displayIdentity;
|
|
334
340
|
if (di && typeof di === "object" && typeof di.value === "string" && di.value.length > 0 && typeof di.kind === "string" && DISPLAY_IDENTITY_KINDS.includes(di.kind)) {
|
|
@@ -616,8 +622,9 @@ function OcSessionProvider({
|
|
|
616
622
|
},
|
|
617
623
|
[cfg.authOrigin, refresh]
|
|
618
624
|
);
|
|
625
|
+
const here = React4__namespace.useSyncExternalStore(onHistory, pageAddress, noPageYet);
|
|
619
626
|
const value = React4__namespace.useMemo(() => {
|
|
620
|
-
const returnTo = defaultReturnTo ??
|
|
627
|
+
const returnTo = defaultReturnTo ?? here;
|
|
621
628
|
return {
|
|
622
629
|
status,
|
|
623
630
|
account,
|
|
@@ -643,7 +650,8 @@ function OcSessionProvider({
|
|
|
643
650
|
addAccountUrl,
|
|
644
651
|
setDisplayIdentity,
|
|
645
652
|
cfg,
|
|
646
|
-
defaultReturnTo
|
|
653
|
+
defaultReturnTo,
|
|
654
|
+
here
|
|
647
655
|
]);
|
|
648
656
|
return /* @__PURE__ */ jsxRuntime.jsx(SessionContext.Provider, { value, children });
|
|
649
657
|
}
|