diff --git a/CHANGELOG.md b/CHANGELOG.md index ecbe159..dd6c65b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,36 @@ site with its own vendored copy (see "Updating your integration" in sitting on an older vendored copy is missing a fix or behavior change it would plausibly want. Newest first. +## 2026-08-04 + +- **CSP fix every self-hosting site needs: add `'wasm-unsafe-eval'` to + `script-src`.** Argon2id (hash-wasm) compiles a WASM module in the crypto + worker; a `script-src 'self'` policy without that keyword blocks + `WebAssembly.compile` and breaks every password flow. The keyword permits + WASM compilation only — JS `eval` stays banned. Related: if your service + worker precaches the widget, fetch with `cache: "reload"` during install — + an asset revalidating to a 304 keeps its OLD stored headers, and a worker + script cached with a stale CSP runs under that stale policy (this shipped + once; see the demo's `sw.js` for the pattern). +- **One login surface, four methods.** Behind a collapsed "More sign-in + options" menu (password stays primary): NIP-07 extension delegation, + NIP-46 remote signers (bunker:// paste + a dot-style nostrconnect QR; + NIP-44 transport only; memory-only sessions), and passkey sign-in + (WebAuthn PRF derives the account credential under a frozen contract — + zero site setup). Alternative-method sessions are signer-only: no vault + (`requestNwcConnection` resolves null), no persistence. +- **Host API additions.** `bitlogin-login` now carries `detail.method` and + `detail.capabilities` (existing `detail.publicKey` unchanged); + `window.bitlogin` gains `activeMethod()` / `activeSession()` — use these + for session state, NOT `isActiveSigner()`, which stays a window.nostr + ownership check and is deliberately false during NIP-07 sessions. + `window.nostr` is now an element-routed provider that follows the active + signer. +- **UI polish.** Screen headers with icon back buttons, right-aligned + "Forgot password?", icon+description option rows with 56px touch targets. +- New widget dependency bundled in: `qrcode-generator` (QR for + nostrconnect). File count and deployment layout unchanged. + ## 2026-07-30 - **Widget: `requestNwcConnection()` — the Connection Vault authorization diff --git a/README.md b/README.md index 9978e50..3a0cc30 100644 --- a/README.md +++ b/README.md @@ -57,9 +57,23 @@ must be deployed side by side — the main script resolves the worker's URL at runtime relative to its own location, so this works from any path, not just the site root. +The widget is a complete login surface, not just the password flow: behind +"More sign-in options" it also offers a NIP-07 signer extension (when one is +detected), a NIP-46 remote signer (bunker:// paste or a scannable +nostrconnect QR for phone signers like Amber), a passkey (WebAuthn PRF — +zero site setup, the passkey deterministically derives the account +credential), §SF10 key import, and phrase recovery. The `bitlogin-login` +event reports `detail.method` (`'bitlogin' | 'nip07' | 'nip46'`) and +`detail.capabilities`, and `window.bitlogin.activeMethod()` / +`activeSession()` answer session state for every method. See +`docs/login-methods.md` and `docs/passkey-login.md`. + Once a user signs in, `window.nostr` is installed with the de facto NIP-07 shape (`getPublicKey`, `signEvent`, `getRelays`, `nip44.encrypt/decrypt`), so -existing Nostr web apps work against it unmodified. Private keys are held in +existing Nostr web apps work against it unmodified — routed to whichever +signer backs the session (the worker for password/passkey accounts, the +worker-held NIP-46 client for remote-signer sessions; NIP-07 sessions leave +the slot to the extension that owns it). Private keys are held in the worker and are not handed to the main thread by the signing APIs — those return public keys, signed events, and ciphertext only. @@ -358,8 +372,10 @@ HKDF, ScalarExpand, AES-256-GCM, JCS, fixed padding buckets). - Phase 2 messaging (NIP-17 inbox/compose) — the NIP-44 primitive is built and tested, but no conversation UI is wired up. -- Phase 3 application redirect-auth flow (§26.3) and NIP-46/NIP-07 browser - extension interop beyond the `window.nostr` shim. +- Phase 3 application redirect-auth flow (§26.3). (NIP-07 and NIP-46 + sign-in, anticipated by §26.4, shipped 2026-08-04 — see + `docs/login-methods.md`; NIP-46 credential *persistence* stays gated on + the external vault review.) - NIP-49 `ncryptsec` export (§28.2) — deliberately left out rather than shipped unverified against official test vectors; plain `nsec`/`npub` (NIP-19) export is implemented instead. diff --git a/docs/TODO.md b/docs/TODO.md index 5ef7d3d..9edc063 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -1,6 +1,32 @@ # BitLogin — open items -Updated: 2026-07-31 +Updated: 2026-08-04 + +## Login-surface expansion (2026-08-04) — shipped, with owner follow-ups + +One login surface, four methods (`login-methods.md`, `passkey-login.md`): +password (primary), NIP-07 extension, NIP-46 remote signer (memory-only), +and passkey via WebAuthn PRF. OAuth on-ramps were built, then removed the +same day for requiring per-site provider registration +(`centralized-onramps.md`, superseded, kept on the record). Also shipped: +the deployment-CSP `'wasm-unsafe-eval'` fix (live password flows were +broken since 2026-07-31), the service-worker `cache: "reload"` precache +fix, `window.bitlogin.activeMethod()/activeSession()`, and welcome/sign-in +UI polish. + +Owner action items: + +1. **Live-fire the NIP-46 QR with Amber** on bitlogin.network (flow was + verified against a fake bunker in tests, not yet against a real phone + signer end-to-end). +2. **Live-fire passkey sign-in on a real device** (Chrome + Google Password + Manager, and once on iOS Safari). PRF cannot be exercised in CI; the + ceremony paths are untested against real authenticators. +3. **Passkey derivation is a new compatibility contract** — + `packages/widget/src/passkey.test.ts` known-answer vectors carry the + same never-update-the-expected-value rule as `knownAnswer.test.ts`. +4. NIP-46 credential *persistence* (vault record) stays gated on the + external security review, as before. ## Entropy audit (2026-07-31) diff --git a/packages/demo/public/llms.txt b/packages/demo/public/llms.txt index d758478..c160035 100644 --- a/packages/demo/public/llms.txt +++ b/packages/demo/public/llms.txt @@ -40,14 +40,7 @@ deployment above. per-credential); passkeys are per-site (rpId) -- cross-site portability is the capsule account's job. Rotating the password is the graduation step to fully self-held. Requires a PRF-capable browser/authenticator; - the widget shows an honest fallback message otherwise. The service acts as the password manager for a REAL - capsule account (it holds the unlock secret, never the key); rows render - under "Use an account you already have" with the custody statement - (" manages your sign-in") as the sub-label, new users can create - a fresh identity or import an existing nsec, and the dashboard nudges - toward claiming the recovery phrase ("Secure your account"). Hosts must - add the service origin to CSP connect-src. Contract and threat model: - docs/centralized-onramps.md. + the widget shows an honest fallback message otherwise. - One login surface, several methods: if the visitor's browser has a NIP-07 signer extension, the welcome screen offers "Use your Nostr extension instead" below the primary username/password path, and every visitor gets