Commit aaeae1e
authored
idp: self-service DELETE /idp/account endpoint (JavaScriptSolidServer#352) (JavaScriptSolidServer#391)
* idp: self-service DELETE /idp/account endpoint (JavaScriptSolidServer#352)
Authenticated owner can now delete their own account via HTTP. Mirrors
the password-rotation pattern from JavaScriptSolidServer#351 (PUT /idp/credentials):
caller authenticates with their existing session, re-proves possession
by sending currentPassword in the body, server resolves the account
from the caller's WebID and deletes it. Owner-only by construction —
the cross-account attack (A authenticated, sends B's password) returns
401 and touches nothing.
Two new shape elements vs. password rotation:
1. Single-user mode rejects with 403. Deleting the single account
would brick the IdP until re-seed; the operator path stays the CLI
(`jss account delete`). Test asserts both 403 status and
account-still-functional after.
2. Optional `purgeData: true` body flag mirrors the CLI's `--purge`.
Removes the pod's filesystem tree at <dataRoot>/<username>/. Path
is normalized + verified to be a proper child of dataRoot
(belt-and-suspenders against future config drift; usernames are
already validated by registration). Default is false — account
record gone, pod data preserved, matches the CLI's default.
Endpoint surface:
DELETE /idp/account
Headers: Authorization (existing session / DPoP)
Body: { currentPassword: string, purgeData?: boolean }
Returns:
200 { ok, webid, purged } — success
400 — missing/invalid currentPassword
401 — unauthenticated, or wrong password
403 — single-user mode, or no account for caller's WebID
Rate-limited 5/min/IP.
UI deferred — the issue allows it; auth for a /idp HTML page is its
own concern (existing /idp landing is unauthenticated). Endpoint +
tests is the unit of value here. Doctor-app or pane work in a follow-up.
Tests cover: unauthenticated, missing field, wrong password, happy
path (subsequent login fails), purgeData=true wipes filesystem,
purgeData=false preserves it, cross-account safety, single-user 403.
8 new tests; 627/627 full suite passes.
* idp: address Copilot review on JavaScriptSolidServer#391 — JSDoc accuracy, purge best-effort
Two fixes per the review:
1. JSDoc no longer claims a 404 failure mode the code doesn't return.
"No account for caller's WebID" lands at 403, not 404 — the caller
had a valid token (so they're identifying themselves), just not for
an account this server holds. Added the rationale to the comment.
2. Purge is now best-effort. fs.remove() can throw (permissions, FS
races, etc.); the account is deleted *before* the purge, so a
throw would have surfaced as a 500 over a partially-completed
deletion (account gone, pod data lingering). New shape:
- try/catch around fs.remove
- on throw: log server-side via request.log.error (with err / path
/ username for the operator log) and continue with purged: false
- response.purged accurately signals outcome; an operator can
finish the cleanup with the CLI `--purge` against the orphaned
directory if needed
- raw err message NOT surfaced to the user — file paths and
permission detail are sensitive
8/8 delete-account tests still pass.
* idp: Copilot review pass 2 on JavaScriptSolidServer#391 — purge by podName, not username
Real bug. createAccount() in src/idp/accounts.js normalizes username
to lowercase (`username.toLowerCase().trim()`), but the pod directory
on disk is created using the original-case input to handleCreatePod.
On case-sensitive filesystems these can differ:
Account record: { username: "alice", podName: "Alice" }
On disk: <dataRoot>/Alice/
The previous purge logic derived its path from account.username, so
it would either no-op (path didn't exist) or hit a different
directory if one happened to exist at the lowercased name. Both
outcomes are wrong: pod data lingers, or the wrong tree is removed.
Fix: use account.podName, falling back to account.username only if
podName is somehow absent (older account records or edge cases).
Same fix applied to the CLI at bin/jss.js:776 — same code path,
same bug, would only have been hidden on case-insensitive devboxes
(macOS default).
Test: new "purgeData: true removes the on-disk pod dir even when it
has uppercase letters" — creates a pod with mixed-case name "Greta…",
verifies the on-disk dir at the mixed-case path is gone after purge.
This would fail on the previous code path on a case-sensitive FS.
Also corrected the inline comment about pod-name validation: it was
describing the slug regex (allows dots), not the pod-name regex
(/^[a-zA-Z0-9_-]+$/, no dots, allows underscores and uppercase).
9/9 delete-account tests pass.
* idp: Copilot review pass 3 on JavaScriptSolidServer#391 — JSDoc match, FS-root-safe purge
Two fixes:
1. JSDoc no longer says `<dataRoot>/<username>/` — corrected to
`<podName>` to match the implementation (which now uses
account.podName, falling back to username only if podName is
absent).
2. The "candidate is a proper child of dataRoot" guard switched from
`candidate.startsWith(root + path.sep)` to `path.relative(root,
candidate)`. The startsWith check failed at filesystem roots: if
DATA_ROOT is `/`, then `root` is `/` (1 char) and `root + path.sep`
is `//` (2 chars), so any legitimate child like `/alice` fails the
prefix check and the purge silently no-ops. path.relative handles
that correctly — and rejects `..` traversal up front, which is
tighter anyway.
9/9 delete-account tests still pass.
* idp: pass 4 on JavaScriptSolidServer#391 — fix the OTHER JSDoc line that still said <username>
I caught the inline comment in pass 3 but missed this higher up in the
function-level JSDoc. Same correction: <username> → <podName> with
fallback note.1 parent a39b9db commit aaeae1e
4 files changed
Lines changed: 501 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
773 | 773 | | |
774 | 774 | | |
775 | 775 | | |
776 | | - | |
| 776 | + | |
| 777 | + | |
| 778 | + | |
| 779 | + | |
777 | 780 | | |
778 | 781 | | |
779 | 782 | | |
780 | | - | |
| 783 | + | |
| 784 | + | |
781 | 785 | | |
782 | 786 | | |
783 | 787 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
5 | 5 | | |
6 | 6 | | |
7 | 7 | | |
8 | | - | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
9 | 11 | | |
10 | 12 | | |
11 | 13 | | |
| |||
272 | 274 | | |
273 | 275 | | |
274 | 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 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
| 382 | + | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
| 391 | + | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
| 409 | + | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
| 415 | + | |
| 416 | + | |
| 417 | + | |
| 418 | + | |
275 | 419 | | |
276 | 420 | | |
277 | 421 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
23 | 23 | | |
24 | 24 | | |
25 | 25 | | |
| 26 | + | |
26 | 27 | | |
27 | 28 | | |
28 | 29 | | |
| |||
279 | 280 | | |
280 | 281 | | |
281 | 282 | | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
282 | 298 | | |
283 | 299 | | |
284 | 300 | | |
| |||
0 commit comments