Commit 768d250
authored
B.2: connect NIP-07 signer and emit Multikey verificationMethod (#2)
* B.2: connect NIP-07 signer and emit Multikey verificationMethod
Adds a second section to the doctor: after diagnostics run, the user
can connect their Nostr signer (xlogin or any window.nostr provider)
and get back a copyable JSON snippet — verificationMethod plus
authentication / assertionMethod entries — to paste into their
profile.
Multikey encoding follows did:nostr's recipe:
"f" + e701 (multicodec varint for secp256k1-pub)
+ parity byte (0x02 default)
+ 32-byte x-only Nostr pubkey
verified against the spec example.
No keys leave the browser; B.3 will close the loop with an in-app
PATCH via Solid-OIDC.
Closes #1.
* Address copilot review on #2
- Multikey docstring: "e70102" → "e701" (constant was already correct;
comment was stale from earlier fix).
- nostrPubkeyToMultikey: validate that input is actually hex, not just
64 chars long.
- doctor.js: only reveal add-key section when the profile fetched
successfully; runAll now returns { checks, profileFetched, docUrl }
so callers can distinguish "diagnostics ran" from "diagnostics found
a real profile to root a snippet against."
- doctor.js: clear stale signer output on connection error so users
can't accidentally copy an outdated snippet after a failed reconnect.
Also corrected the verificationMethod controller while in the file:
the spec / JSS Phase A profile shape uses the WebID-with-fragment as
controller (matching the profile's outer `controller` predicate), not
the bare document URL. Updated lws-cid.js's VM-controller check to
compare against the profile's @id with fragment so the validator and
generator agree.
* Address copilot pass 2 on #2
- doctor.js: thread the canonical WebID (with fragment) through to the
Multikey builder. runAll now returns the absolutized profile @id as
`webId`, and the connect handler uses that — not the fragmentless
docUrl — when calling buildNostrVerificationMethod. Without this the
earlier controller-with-fragment fix was undone at the callsite.
- doctor.js: drop the unused `docUrl` arg from runLwsCidChecks call.
- lib/multikey.js: error message no longer says "lowercase hex" since
normalizeHex lowercases first; uppercase input is accepted.
* Address copilot pass 3 on #2
- doctor.js: snippet target shown to the user is the document URL
(fragmentless) — that's the actual PUT/PATCH target. The VM's
controller still uses the WebID-with-fragment. Threaded docUrl
through renderSnippet alongside webId.
- doctor.js: clearSignerOutput now resets the connect-button label
and copy status, so a re-run of diagnostics doesn't leave stale
"Reconnect signer" / copy text behind.
- doctor.css: dropped dead "#snippet pre" rule (snippet IS the code
inside the pre, so the descendant selector matches nothing).
* Address copilot pass 4 on #2
- lib/lws-cid.js: VM controllers are now compared against the
profile's declared `controller` (absolutized), with fallback to
`@id`. The previous code used `@id` unconditionally, which would
incorrectly fail otherwise-valid delegated-control profiles. Also
fixes the converse: a delegated profile with VMs still pointing at
the original WebID now correctly fails (verified with a mismatched
test case).
- doctor.css + doctor.js: copy-status no longer renders error text in
the success color. Added `.success` and `.error` modifier classes
with a neutral default; click handler sets the appropriate one.
* Address copilot pass 5 on #2
JSON-LD lets `controller` be a string, object with @id/id, OR an
array of either form. The previous code assumed string|@id-object,
so an array silently produced an undefined expected controller and
disabled the mismatch check entirely.
Added normalizeControllers() helper that handles all four shapes
(string, object-with-@id, object-with-id-shorthand, array) and
returns a list of absolutized IRIs. Both the profile's outer
controller and each VM's controller are normalized through it; a
VM passes if any of its controllers is in the profile's expected
controller set.
Verified against five edge cases: array containing the WebID
(passes), object-with-id shorthand (passes), VM matching second
array entry (passes), VM matching neither (correctly fails), and
VM with array-valued controller (passes when intersection is
non-empty).
* Address copilot pass 6 on #2
- doctor.js: harden canonical-WebID derivation. Wrap URL parsing in
try/catch so a malformed profile @id can't crash diagnostics; only
adopt the profile @id when its fragmentless form matches the
fetched docUrl, otherwise fall back to the user-supplied URL. This
prevents a snippet whose VM id is rooted at one document while the
UI tells the user to PATCH a different one.
- lib/lws-cid.js: route the self-control check through
normalizeControllers too, so all four JSON-LD shapes (string,
object-with-@id, object-with-id, array) yield consistent results.
Previously, controller={id: ...} or array forms produced an
undefined comparison and a misleading warn. Empty/unresolvable
controller now reports "does not resolve to a usable IRI" rather
than silently degrading.
* Address copilot pass 7 on #2
- README: "green/red checklist" → "pass/warn/fail/skip checklist".
The actual UI has four states; binary phrasing was misleading.
- lib/lws-cid.js: skip the self-control comparison when @id is
missing rather than emitting "@id=null" in a delegated-control
warning. The earlier "Profile has @id" check has already failed,
and "controller === @id" isn't meaningful without an @id to
compare against.
* Address copilot pass 8 on #2
- doctor.js: hideAddKeySection() at the start of the submit handler
(before awaiting runAll). Without this, a stale snippet from a
previous run remained visible — and clickable — during a new run,
so a user could copy or apply a snippet rooted at the wrong WebID.
- doctor.js: clearSignerOutput() when revealing the add-key section.
Without this, after diagnostics complete on a NEW WebID, the old
pubkey/snippet from the previous run was still visible until the
user clicked reconnect — same accidental-copy hazard.
* Address copilot pass 9 on #2
- index.html: signer-status gets role="status" + aria-live="polite"
so screen readers announce state changes (signer detected /
connected / error). Decorative dot marked aria-hidden.
- doctor.js: re-check window.nostr presence at click time. The
provider can be uninstalled or disabled between detect-on-reveal
and the click, which would have thrown a raw TypeError. Now
surfaces the same "install xlogin and reload" message and disables
the button.
- doctor.css: :focus-visible outline on .add-key buttons so keyboard
navigation has a visible focus indicator.
- README: VM-controller wording updated to reflect the validator's
actual behavior (matches profile's declared controller, with
fallback to @id), since the previous wording implied a stricter
always-the-WebID rule.
* Address copilot pass 10 on #2
The generator was hard-coding VM controller to the WebID, but the
validator (in pass 4) was already comparing each VM controller
against the profile's declared `controller` (with @id fallback).
For delegated-control profiles those differ, so the snippet would
fail my own VM-controller check.
- lib/multikey.js: buildNostrVerificationMethod accepts an explicit
`controller` arg, defaulting to webId. Self-control behavior is
unchanged; delegated-control callers can now pass the profile's
declared controller through.
- lib/lws-cid.js: export normalizeControllers so callers outside the
validator can derive a canonical controller IRI from JSON-LD's
string/object/array shapes.
- doctor.js: capture the profile's controller during runAll using
normalizeControllers (first IRI, fallback to canonical WebID),
thread it through state and into buildNostrVerificationMethod.
Verified end-to-end: in both self-controlled and delegated-controlled
profiles, the generated VM is accepted by runLwsCidChecks when
merged back into the source profile.
* Address copilot pass 11 on #2
- doctor.js: bail with a clean fail-check when JSON.parse returns
null, a primitive, or an array. Previously these would crash
downstream on profile['@id'] / profile.controller access and
surface as a generic "Diagnostics crashed".
- lib/multikey.js: validate that webId is a non-empty string at the
buildNostrVerificationMethod entry point. Also harden stripHash to
not throw on non-string input — defensive since the helper is
exported for standalone use.1 parent 8fd2726 commit 768d250
6 files changed
Lines changed: 551 additions & 40 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
6 | 6 | | |
7 | 7 | | |
8 | 8 | | |
9 | | - | |
| 9 | + | |
10 | 10 | | |
11 | | - | |
| 11 | + | |
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
18 | | - | |
| 18 | + | |
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
| 25 | + | |
| 26 | + | |
25 | 27 | | |
26 | 28 | | |
27 | | - | |
| 29 | + | |
| 30 | + | |
28 | 31 | | |
29 | | - | |
30 | | - | |
| 32 | + | |
31 | 33 | | |
32 | 34 | | |
33 | 35 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
184 | 184 | | |
185 | 185 | | |
186 | 186 | | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
| 202 | + | |
| 203 | + | |
| 204 | + | |
| 205 | + | |
| 206 | + | |
| 207 | + | |
| 208 | + | |
| 209 | + | |
| 210 | + | |
| 211 | + | |
| 212 | + | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
| 276 | + | |
| 277 | + | |
| 278 | + | |
| 279 | + | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
0 commit comments