Observed
createAccount(...) called without a webId stores the account anyway, and the WebID uniqueness index picks up the literal JSON key "undefined". The first webId-less account succeeds; every subsequent one throws:
Error: WebID already has an account
at createAccount (src/idp/accounts.js:104)
In practice this bites on the first createAccount call an integration test makes, because server startup already creates a webId-less account itself (the remoteStorage user me) — so the "undefined" slot is consumed before user code runs.
Repro
process.env.DATA_ROOT = fs.mkdtempSync(path.join(os.tmpdir(), 'acct-'));
const { createAccount } = await import('javascript-solid-server/src/idp/accounts.js');
await createAccount({ username: 'lefty', password: 'x', podName: 'lefty' }); // ok — indexes webId "undefined"
await createAccount({ username: 'rocco', password: 'x', podName: 'rocco' }); // throws: WebID already has an account
(With a running createServer({ idp: true, ... }) instance the first call already throws, courtesy of the auto-created me account.)
Why it happens
src/idp/accounts.js createAccount destructures webId and does:
const existingByWebId = await findByWebId(webId); // webIdIndex[undefined] → "undefined" key
if (existingByWebId) throw new Error('WebID already has an account');
then indexes the new account under the same coerced "undefined" key.
Suggested fix (either)
- Skip the WebID uniqueness check and index write entirely when
webId is absent (if (webId) { ... } around both), or
- Derive a WebID at creation time from
podName + issuer so every account has a real one.
Option 1 is a two-line guard; option 2 is nicer for consumers (accounts always resolvable by WebID) but needs the issuer available in createAccount.
Workaround
Always pass an explicit webId. (That's what the OMERTÀ plugin's integration tests do now, with a comment pointing here.)
Found while building the OMERTÀ plugin (third consumer of the #206 seams — client-authoritative static app, after Tideholm and Bridge); findings log in solid-apps/mafia jss-plugin/NOTES.md.
Observed
createAccount(...)called without awebIdstores the account anyway, and the WebID uniqueness index picks up the literal JSON key"undefined". The first webId-less account succeeds; every subsequent one throws:In practice this bites on the first
createAccountcall an integration test makes, because server startup already creates a webId-less account itself (the remoteStorage userme) — so the"undefined"slot is consumed before user code runs.Repro
(With a running
createServer({ idp: true, ... })instance the first call already throws, courtesy of the auto-createdmeaccount.)Why it happens
src/idp/accounts.jscreateAccountdestructureswebIdand does:then indexes the new account under the same coerced
"undefined"key.Suggested fix (either)
webIdis absent (if (webId) { ... }around both), orpodName+ issuer so every account has a real one.Option 1 is a two-line guard; option 2 is nicer for consumers (accounts always resolvable by WebID) but needs the issuer available in
createAccount.Workaround
Always pass an explicit
webId. (That's what the OMERTÀ plugin's integration tests do now, with a comment pointing here.)Found while building the OMERTÀ plugin (third consumer of the #206 seams — client-authoritative static app, after Tideholm and Bridge); findings log in
solid-apps/mafiajss-plugin/NOTES.md.