Skip to content

idp: createAccount without a webId poisons the WebID index — second webId-less account collides #608

Description

@melvincarvalho

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)

  1. Skip the WebID uniqueness check and index write entirely when webId is absent (if (webId) { ... } around both), or
  2. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions