Skip to content
This repository has been archived by the owner on Feb 12, 2024. It is now read-only.

HTTP API of jsipfs daemon rejects IpfsHttpClient access on NW.js window #3277

Closed
bellbind opened this issue Sep 10, 2020 · 7 comments
Closed
Labels
kind/bug A bug in existing code (including security flaws)

Comments

@bellbind
Copy link

bellbind commented Sep 10, 2020

  • Version: 0.50.2
  • Platform: Darwin 19.6.0 Darwin Kernel Version 19.6.0: Thu Jun 18 20:49:00 PDT 2020; root:xnu-6153.141.1~1/RELEASE_X86_64 x86_64
    • (macOS 10.15.6)
  • Subsystem: http api

Severity: Low

(if HTTP API of jsipfs is optional)

Description:

IpfsHttpClient on NW.js cannot access HTTP API of jsipfs daemon, as:

Failed to load resource: the server responded with a status of 403 (Forbidden)
HTTPError: Forbidden
    at Object.p [as handleError] (https://cdn.jsdelivr.net/npm/ipfs-http-client/dist/index.min.js:2:136801)
    at async m.fetch (https://cdn.jsdelivr.net/npm/ipfs-http-client/dist/index.min.js:2:80727)
    at async Object.version (https://cdn.jsdelivr.net/npm/ipfs-http-client/dist/index.min.js:2:256731)
    at async chrome-extension://gmmggpmjfbeknnfebndnebnomimnleok/index.html:11:31

(On Chrome and Firefox, IpfsHttpClient can access HTTP API of same jsipfs daemon)

Steps to reproduce the error:

  1. Error example for nw.js with IpfsHttpClient:

package.json:

{
  "name": "access-ipfs-api-from-nwjs",
  "dependencies": {
    "ipfs": "0.50.2",
    "nw": "0.48.1-sdk"
  },
  "main": "index.html",
  "scripts": {
    "jsipfs": "jsipfs daemon",
    "start": "nw"
  }
}

index.html:

<html>
  <head>
    <script type="module">
if (window.nw) window.nw.Window.get().showDevTools();

import "https://cdn.jsdelivr.net/npm/ipfs-http-client/dist/index.min.js";

(async () => {
  const node = window.node = IpfsHttpClient("http://127.0.0.1:5002");
  const h2 = document.createElement("h2");
  h2.append(`IPFS version: ${(await node.version()).version}`);
  document.body.append(h2, `Peer ID: ${(await node.id()).id}`);
})().catch(console.error);
    </script>
  </head>
  <body></body>
</html>
  1. Setup:

npm install on the directory of the package.json and the index.html:

$ npm install
...
  1. Start jsipfs daemon:
$ $(npm bin)/jsipfs daemon
Initializing IPFS daemon...
js-ipfs version: 0.50.2
System version: x64/darwin
Node.js version: 14.9.0
Swarm listening on /ip4/127.0.0.1/tcp/4002/p2p/Qmbywf2eDSoksSj4rBSBfr95Em5naYsg9hhwXGNYfSnYD3
Swarm listening on /ip4/192.168.10.6/tcp/4002/p2p/Qmbywf2eDSoksSj4rBSBfr95Em5naYsg9hhwXGNYfSnYD3
Swarm listening on /ip4/127.0.0.1/tcp/4003/ws/p2p/Qmbywf2eDSoksSj4rBSBfr95Em5naYsg9hhwXGNYfSnYD3
API listening on /ip4/127.0.0.1/tcp/5002/http
Gateway (read only) listening on /ip4/127.0.0.1/tcp/9090/http
Web UI available at http://127.0.0.1:5002/webui
Daemon is ready
  1. Run index.html on nw.js:
$ $(npm bin)/nw

Then, a main window and a DevTools window are opened.

  • Console tab on DevTools window: display above errors: Failed to load resource...
  • Main window: blank as a result of failure to access with HTTP API

My Experiments:
  1. The index.html works on chrome-87 or on firefox-80:
  • (The page displays a IPFS version on the jsipfs daemon and its Peer ID)
  1. On the Console tab of DevTools window, await fetch("http://127.0.0.1:8000/", {method: "POST"}) can successfully access to nodejs http server:
  • server.cjs:
const http = require("http");
const port = 8000;
http.createServer((req, res) => {
  console.log(req.method, req.url, JSON.stringify(req.headers));
  res.writeHead(200, {"content-type": "text/html;charset=utf-8"});
  res.end("<html><head></head><body>Hello node</body></html>");
}).listen(port, () => {
  console.log(`http://127.0.0.1:${port}/`);
});
  • Result on Console:
await fetch("http://127.0.0.1:8000/", {method: "POST"})
Response {type: "basic", url: "http://127.0.0.1:8000/", redirected: false, status: 200, ok: true, ...}
  • Output from the server:
$ node server.cjs
http://127.0.0.1:8000/
POST / {"host":"127.0.0.1:8000","connection":"keep-alive","content-length":"0","user-agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36","accept":"*/*","sec-fetch-site":"none","sec-fetch-mode":"cors","sec-fetch-dest":"empty","accept-encoding":"gzip, deflate, br","accept-language":"ja,en-US;q=0.9,en;q=0.8"}
  1. fetch HTTP API URL from the Console tab of Devtools window
res = await fetch("http://127.0.0.1:8000/", {method: "POST"})
Response {type: "basic", url: "http://127.0.0.1:5002/api/v0/version", redirected: false, status: 403, ok: false, ...}

Object.fromEntries(res.headers.entries())
  access-control-expose-headers: "WWW-Authenticate,Server-Authorization"
  cache-control: "no-cache"
  connection: "keep-alive"
  content-length: 47
  content-type: "application/json; charset=utf-8"
  date: "Thu, 10 Sep 2020 07:26:36 GMT"
  keep-alive: "origin"

await res.text()
"{"Message":"Forbidden","Code":1,"Type":"error"}"
@bellbind bellbind added the need/triage Needs initial labeling and prioritization label Sep 10, 2020
@welcome
Copy link

welcome bot commented Sep 10, 2020

Thank you for submitting your first issue to this repository! A maintainer will be here shortly to triage and review.
In the meantime, please double-check that you have provided all the necessary information to make this process easy! Any information that can help save additional round trips is useful! We currently aim to give initial feedback within two business days. If this does not happen, feel free to leave a comment.
Please keep an eye on how this issue will be labeled, as labels give an overview of priorities, assignments and additional actions requested by the maintainers:

  • "Priority" labels will show how urgent this is for the team.
  • "Status" labels will show if this is ready to be worked on, blocked, or in progress.
  • "Need" labels will indicate if additional input or analysis is required.

Finally, remember to use https://discuss.ipfs.io if you just need general support.

@achingbrain
Copy link
Member

This is because the request that is sent uses a browser user agent but contains no referrer or origin and fails the test ported from ipfs/go-ipfs-cmds#193

cc @lidel

@achingbrain achingbrain added kind/bug A bug in existing code (including security flaws) status/ready Ready to be worked need/analysis Needs further analysis before proceeding and removed need/triage Needs initial labeling and prioritization status/ready Ready to be worked labels Sep 10, 2020
@lidel
Copy link
Member

lidel commented Sep 10, 2020

@bellbind you need to safelist specific Origin to give it access to the API inside a web browser context:

$ jsipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '["http://origin-you-want-to-safelist.example.com", "http://127.0.0.1:5001"]'
$ jsipfs config --json API.HTTPHeaders.Access-Control-Allow-Methods '["PUT", "POST"]'

Example:

$ curl -X POST -A "Mozilla" http://127.0.0.1:5002/api/v0/id
{"Message":"Forbidden","Code":1,"Type":"error

$ curl -X POST -A "Mozilla" -H "Origin: http://origin-you-want-to-safelist.example.com"  http://127.0.0.1:5002/api/v0/
{ "ID": ... }

@achingbrain
Copy link
Member

Until #3275 lands, the CORS origin is effectively set to * so changing the config won't help.

NW.js is an Electron-like environment that doesn't send a referer or origin and uses a generic browser user agent string (e.g. doesn't append NW.js or whatever to it like electron does).

My point is, it's like the electron renderer stuff we discussed on ipfs/go-ipfs-cmds#201 except we can't use the user agent to allow the request through.

@lidel
Copy link
Member

lidel commented Sep 10, 2020

@achingbrain I see – thanks for this context!

The way I see it:

  • @bellbind needs to wait for fix: disable cors by default #3275 to ship, then they will be able to set correct CORS headers to make it work in "generic browser context" (UserAgent starting with Mozilla)
  • If NW.js does not send correct Origin/Referer, then that is a bug in NW.js, and a separate bug needs to be filled there (either to fix Origin/Referer, or change UserAgent to add Electron at the end to indicate it is Electron-like environment)

@achingbrain
Copy link
Member

Even after #3275 ships configuring the CORS allowed origins still won't work because NW.js doesn't send an origin or a referer.

What needs to happen is for either NW.js to not use the default user agent since we can't tell it apart from regular browsers, or for us not to use the User Agent to allow or deny requests.


@bellbind in the meantime you can work around this by changing the default user agent for outgoing requests.

Add a user-agent field to your package.json that contains any string that doesn't start with 'Mozilla':

{
  "name": "access-ipfs-api-from-nwjs",
  "dependencies": {
    "ipfs": "0.50.2",
    "nw": "0.48.1-sdk"
  },
  "main": "index.html",
  "scripts": {
    "jsipfs": "jsipfs daemon",
    "start": "nw"
  },
  "user-agent": "falalala"
}

@achingbrain achingbrain removed the need/analysis Needs further analysis before proceeding label Sep 10, 2020
@bellbind
Copy link
Author

Thank you @achingbrain.

It worked with adding "user-agent": "NW.js" in package.json.

bellbind added a commit to bunsanweb/stellar-runtime that referenced this issue Sep 11, 2020
- HTTP API of jsipfs requires some `User-Agent` request header field to access
  - see: ipfs/js-ipfs#3277
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
kind/bug A bug in existing code (including security flaws)
Projects
None yet
Development

No branches or pull requests

3 participants