Skip to content

did:nostr resolver should prefer local well-known endpoint before external #423

@melvincarvalho

Description

@melvincarvalho

Symptom

When a user signs in to their own JSS pod via NIP-98 (did:nostr-based SSO) and then tries to write, they get 401 Unauthorized. SSO appears to succeed (UI shows logged-in state), but writes fail.

Server-side log:

```
DID resolution error for : fetch failed
```

WAC then denies the write because JSS cannot resolve the requesting did:nostr to the WebID that the ACL grants access to.

Cause

src/auth/did-nostr.js has:

```js
const DEFAULT_DID_RESOLVER = 'https://nostr.social/.well-known/did/nostr';
```

The resolver always fetches the DID document from the external nostr.social endpoint, even when the user is signing in to a JSS instance that hosts that user's pod and therefore already has the authoritative DID document for them locally (served by src/idp/well-known-did-nostr.js).

When the external endpoint is unavailable — currently true for nostr.social (SSL certificate expired at time of writing) — DID resolution fails entirely, and the most common SSO case (user signed into their own pod) breaks.

Verified behaviour

Local endpoint serves the correct DID doc:

```
$ curl -sS https:///.well-known/did/nostr/
HTTP/1.1 200 OK
content-type: application/did+json
{
"id": "did:nostr:",
"alsoKnownAs": ["https:///profile/card.jsonld#me"],
"verificationMethod": [...],
"authentication": [...]
}
```

External endpoint fails:

```
$ curl -sS https://nostr.social/.well-known/did/nostr/
curl: (60) SSL certificate problem: certificate has expired
```

Fix

src/auth/did-nostr.js should:

  1. Try the local well-known endpoint first when the pod hosts the relevant pubkey (i.e. the pubkey index built by src/idp/well-known-did-nostr.js contains the pubkey). This is also what the well-known-did-nostr.js doc-comment describes: "making the pod its own authoritative DID resolver for its accounts."
  2. Fall back to external resolvers (nostr.social, nostr.rocks, etc.) only when the pubkey is not local.
  3. Optional: surface the resolver source in the error message (local-miss, external-fetch-failed, etc.) for easier diagnosis.

Benefits

  • Eliminates the most common 401 failure — user signed into their own pod auths cleanly even when external resolvers are down
  • Removes external dependency for the local case — most pod-auth traffic no longer hits the public internet
  • Faster — no external round-trip on every NIP-98 auth check
  • Aligned with the existing architecturewell-known-did-nostr.js is already in place; this just makes the resolver use it locally before reaching out

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    agenticIssues and PRs related to agentic / LLM-builder / agent-friendly use cases

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions