| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
auth.js at the package root is the stable import path for apps that authenticate their own traffic (appPaths mounts from #582, external compositions): import { getAgent } from 'javascript-solid-server/auth.js'; const webId = await getAgent(request); // string | null Thin wrapper over getWebIdFromRequestAsync, so it covers every token scheme the server accepts (IdP Bearer, Solid-OIDC DPoP, Nostr NIP-98, LWS10-CID) and never throws on bad credentials. Authentication only, deliberately not authorization — apps under an app path own their own permissioning. Everything in src/ stays internal and free to move; this file is the contract, pinned by test/auth-agent.test.js. Pre-loader shape of the seam: when #206's loader lands, api.auth.getAgent is this function. Docs: the appPaths section now shows the public import instead of the internal path. Full suite 1006 green.
There was a problem hiding this comment.
This PR introduces a stable, root-level public authentication seam (javascript-solid-server/auth.js) so external apps/plugins can map an incoming request to an authenticated WebID without importing internal src/auth/* modules.
Changes:
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
| auth.js | New public export surface providing getAgent(request) wrapper around internal auth resolution. |
| test/auth-agent.test.js | New contract/integration test for the public getAgent behavior. |
| docs/configuration.md | Docs updated to point app-path handlers at the new public accessor instead of internal imports. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Sorry, something went wrong.
| it('resolves a real IdP Bearer token to the account WebID', async () => { | ||
| await fs.emptyDir(TEST_DATA_DIR); | ||
| server = createServer({ | ||
| logger: false, | ||
| forceCloseConnections: true, | ||
| root: TEST_DATA_DIR, | ||
| idp: true, | ||
| idpIssuer: 'http://127.0.0.1:0', // patched after listen below | ||
| }); | ||
| await server.listen({ port: 0, host: '127.0.0.1' }); | ||
| baseUrl = `http://127.0.0.1:${server.server.address().port}`; | ||
|
|
There was a problem hiding this comment.
Fixed — port reserved up front and passed as the real idpIssuer (adopted the well-known-did-nostr pattern you pointed at). The token/WebID pair the test asserts is now issued against the actual listening origin.
Sorry, something went wrong.
| * Resolve the authenticated agent of a request. | ||
| * @param {object} request - Fastify request (or any object with `headers`) | ||
| * @returns {Promise<string|null>} verified WebID, or null when anonymous / |
There was a problem hiding this comment.
Agreed, the doc oversold it. JSDoc now states the split explicitly: Bearer reads only headers; DPoP/NIP-98/LWS-CID verification also reads method/url/protocol/hostname — so pass the real Fastify request.
Sorry, something went wrong.
The idpIssuer was a bogus http://127.0.0.1:0 with a stale 'patched later' comment — now a port is reserved up front and the IdP gets its real issuer (the well-known-did-nostr test's pattern). getAgent's JSDoc no longer claims 'any object with headers': Bearer only reads headers, but DPoP/NIP-98/LWS-CID verification also reads method, url, protocol and hostname — pass the real request.
| export async function getAgent(request) { | ||
| try { | ||
| const { webId } = await getWebIdFromRequestAsync(request); | ||
| return webId || null; | ||
| } catch { | ||
| return null; | ||
| } | ||
| } |
There was a problem hiding this comment.
Right diagnosis, deliberate behavior: did:nostr agents are first-class in JSS, so filtering them to null would silently lock Nostr-authenticated users out of every app built on this seam — the plugin-zero consumer (Tideholm) explicitly wants them as players. What was wrong was the doc claiming 'WebID'. The contract now states it plainly: getAgent returns the verified agent identifier — an HTTP(S) WebID, or a did:nostr DID for NIP-98 agents without a WebID mapping — which is why it's named getAgent rather than getWebId. Callers who need strictly-HTTP WebIDs can filter on the prefix; the seam won't decide that for them.
Sorry, something went wrong.
Copilot correctly spotted that NIP-98 can yield did:nostr identifiers where no WebID mapping exists. That's intended, not filtered: DID agents are first-class in JSS, and locking them out would silently exclude Nostr-authenticated users from every app. The contract docs now say what the function actually promises — an HTTP(S) WebID or a did:nostr DID — which is why the seam is named getAgent, not getWebId. Apps key users on the identifier string either way.
| * Covers every token scheme the server itself accepts, uniformly: | ||
| * IdP-issued Bearer tokens, Solid-OIDC DPoP, Nostr NIP-98 signatures, | ||
| * and LWS10-CID. The returned identifier is usually an HTTP(S) WebID; | ||
| * for NIP-98 it can be a `did:nostr:...` DID when no WebID mapping |
There was a problem hiding this comment.
Added — the list now includes WebID-TLS client certificates (and says 'credential scheme' rather than 'token scheme', since TLS isn't a token). Verified against token.js: the async path does try webIdTlsAuth when a client certificate is present.
Sorry, something went wrong.
| @@ -0,0 +1,97 @@ | |||
| /** | |||
| * getAgent — public request → WebID accessor (#584). | |||
There was a problem hiding this comment.
Updated — the test header now says agent-identifier and spells out the WebID-or-did:nostr contract, matching auth.js and the PR description.
Sorry, something went wrong.
| const port = await new Promise((resolve) => { | ||
| const probe = net.createServer(); | ||
| probe.listen(0, '127.0.0.1', () => { | ||
| const p = probe.address().port; | ||
| probe.close(() => resolve(p)); | ||
| }); | ||
| }); |
There was a problem hiding this comment.
Fixed — the probe now rejects on 'error' so a failed listen fails the test with the actual error instead of hanging. Also ported the same fix to the Tideholm demo where this pattern originated.
Sorry, something went wrong.
Public auth seam (#584, #586): import { getAgent } from 'javascript-solid-server/auth.js' resolves a request to its verified agent identifier — an HTTP(S) WebID or a did:nostr DID — across every credential scheme (IdP Bearer, Solid-OIDC DPoP, Nostr NIP-98, LWS10-CID, WebID-TLS). The stable contract for apps that authenticate their own traffic under appPaths mounts (#582, shipped in 0.0.213); everything in src/ remains internal. Second plugin-system seam from #206; remaining: #583 raw-body mode, the #206/#564 loader.
| Back | FazBrowse Home | New Git URL |
Closes #584. Second seam from the plugin-zero work (#206), following the pattern #582/#585 set: smallest change, contract test, real consumer waiting.
What
auth.js at the package root — the stable public import for applications that authenticate their own traffic:
Thin wrapper over getWebIdFromRequestAsync, so it uniformly covers every token scheme the server itself accepts (IdP Bearer, Solid-OIDC DPoP, Nostr NIP-98, LWS10-CID) and never throws on bad credentials. Authentication only, deliberately not authorization — apps under an appPaths prefix (#582) own their own permissioning.
Why a root file
Everything under src/ stays internal and free to refactor; auth.js is the contract. When the #206 loader lands, api.auth.getAgent hands plugins this same function.
Tests
test/auth-agent.test.js pins the contract: anonymous → null, malformed credentials → null (never a throw), real IdP Bearer token → the account's WebID. Scheme-specific coverage stays with the existing auth suites. Full suite: 1006 pass.
Consumer
The Tideholm plugin-zero adapter migrates onto this import the moment it's published (per #584's acceptance list) — removing the last internal-path import in the reference plugin.