Nostr WoT
IntermediateSecurityPermissionsAuthentication

Know which backend you are authenticating to

Understand exact URL and method permissions, separate API domains, iframe protection and the limits of authentication forwarding.

·5 min read
Know which backend you are authenticating to

Nostr WoT separates permission to use your identity on a website from permission to authenticate that identity to an HTTP service. A website and its API can live on different domains. The approval must describe the service receiving the proof, not assume that it is the website in your address bar.

This guide covers Nostr WoT 0.8.7.

For WebSocket relay authentication, read the separate relay authentication guide.

Different domains are legitimate; permission still has a destination

Consider a fictional client at https://client.example whose API is https://api.client.example. A request to authenticate a POST to https://api.client.example/session can be legitimate. Blocking every different hostname would break this architecture.

Instead, Nostr WoT keeps the requesting website and the signed destination separate. A remembered HTTP approval is specific to:

Permission boundaryExample
Your accountThe account approving the request
Requesting website originhttps://client.example
Exact signed URL, including query texthttps://api.client.example/session
HTTP methodPOST

That approval does not also authorize /delete-account, another method, another port, another website or another account. A different query string requires its own approval. Same-origin authentication follows the same rule. Older saved origin-wide HTTP allows require consent again; older denials remain effective.

The client/backend directory helps explain familiar services. It is informational: adding an entry cannot authorize a backend or skip consent. Contributors can propose registry entries.

What you see and control

The prompt identifies the requesting site and the authentication destination. Use Approve or Reject for the current request. The arrow menu offers Approve always for this exact site/account/URL/method, or Reject always. HTTP authentication has no “all sites” allowance.

The code button opens the raw event for inspection. Ordinary bulk signing approval excludes authentication requests so destination-specific consent is not lost in a mixed queue. At the bottom of Permissions, the authentication table shows remembered decisions for the active account and lets you revoke them. Revoking permission prevents future use; it cannot recall a proof already sent or log you out of a session the backend has already created.

What the extension verifies

The requester comes from browser-provided document identity, not a website's claimed origin parameter. Authentication requires a verified top-level frame; embedded frames, opaque origins and inconsistent document origins are refused. HTTPS is required outside explicit loopback development exceptions. An iframe cannot borrow a trusted parent's authentication permission.

The event parser rejects ambiguous required tags, invalid URLs, unexpected content and stale timestamps. Optional origin and client-origin tags must match the actual requesting origin. General signing permission does not bypass these authentication approvals. Site access, account-session validity and permissions are rechecked after waiting for approval or unlock.

With a NIP-46 bunker, the returned signature must belong to the expected account and match the approved event's kind, timestamp, content and ordered tags. A valid signature on a different event is rejected.

Extra protection for native wallet operations

The companion LNbits-proxy uses a stricter v2 contract for provisioning/recovering a wallet and claiming/releasing an address. These are privileged operations, not an ordinary website login. The extension's generic website signer refuses tokens for the native wallet service's sensitive management paths.

In the dedicated wallet flow, the proof binds the exact URL, method and hash of the exact request body. A backend-issued challenge lasts 60 seconds and can be consumed once. A separate transaction token travels in its own header; only its hash is signed. The server verifies these bindings and the signature before an atomic nonce consumption. Changing the operation body invalidates the proof.

Browser requests use an exact configured HTTPS origin allowlist and a matching signed client-origin value. The native flow still requires the proof and separate transaction token. Public LNURL discovery retains its separate policy. This is an application-specific contract, not a new requirement imposed on all Nostr APIs. The wire specification and rollout guide has the developer details. Deploy the compatible backend before publishing the extension: retired routes return HTTP 426, and the client deliberately does not downgrade.

What the proof establishes

NIP-98 binds a signed HTTP proof to its URL and method; a conforming backend must check those values. The wallet contract additionally requires body binding and one-use transactions. For other backends, the extension cannot force the server to validate correctly.

A proof for backend A should not be accepted by backend B. However, someone who holds a valid proof and any required transaction token can forward them to the intended backend before their first use. CORS restricts browsers; it cannot stop arbitrary server-to-server requests. An origin tag is not cryptographic browser attestation, since another signer could sign a false claim.

Likewise, malicious code running inside a trusted top-level site shares that site's authority. These controls reduce the scope of consent and reject concrete forms of substitution and replay; they do not promise to eliminate phishing, compromised sites, or every form of authentication forwarding.

Inspect the implementation

Stay Updated

Get news about published Nostr WoT releases, new features, and integrations.

You will receive the newsletter in English.

We store your email address and preferred language to send you the newsletter.

Newsletters