arctic icon indicating copy to clipboard operation
arctic copied to clipboard

Fix createOAuth2Request

Open SamuelBoerlin opened this issue 9 months ago • 3 comments

While using Pangolin which uses this library I ran into the following errors:

2025-05-20T20:09:39.610Z [error]: Failed to send request
Stack: Error: Failed to send request
    at sendTokenRequest (file:///app/node_modules/arctic/dist/request.js:76:15)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async OAuth2Client.validateAuthorizationCode (file:///app/node_modules/arctic/dist/client.js:71:24)
    at async gg (file:///app/dist/server.mjs:31:139872)
  [cause]: TypeError: Cannot perform ArrayBuffer.prototype.slice on a detached ArrayBuffer
      at ArrayBuffer.slice (<anonymous>)

which seems to originate from the request created here and the subsequent fetch in sendTokenRequest.

I'm not familiar with Node and have no idea what exactly is going on here, but after applying the changes in this PR it works fine.


DO NOT DELETE THIS SECTION.

Thank you for creating a pull request!

If your pull request is just making changes to the docs, please create it against the main branch.

If your pull request is making changes to the library source code, please create it against the next branch. If your pull request adds a new feature to the library, please open a new issue first.

If you're unsure, you can just create it against the main branch.

  • [x] Please tick this box if you’ve read and understood this section..

SamuelBoerlin avatar May 20 '25 20:05 SamuelBoerlin

Can you share what version of Node.js are you using?

pilcrowonpaper avatar May 21 '25 10:05 pilcrowonpaper

Can you share what version of Node.js are you using?

It's version v20.19.1 from this node:20-alpine docker image.

SamuelBoerlin avatar May 21 '25 11:05 SamuelBoerlin

I may have narrowed it down to the combination of the usage of NODE_TLS_REJECT_UNAUTHORIZED=0, which I'm using as I'm currently testing with self-signed certs, and the request resulting in a redirect. I'm not sure if NODE_TLS_REJECT_UNAUTHORIZED=0 is actually necessary for the reproduction as I cannot try without it atm.

Minimal reproduction:

docker run -e "NODE_TLS_REJECT_UNAUTHORIZED=0" --rm node:20.19.1-alpine -e "let body = new URLSearchParams({ 'abc': 'def' }); let bodyBytes = new TextEncoder().encode(body.toString()); let req = new Request('https://self-signed-endpoint/api/oauth/token/', { method: 'POST', body: bodyBytes }); let resp = await fetch(req); console.log(resp);"

Note that https://self-signed-endpoint/api/oauth/token/ must use a self signed, untrusted, cert and redirect to https://self-signed-endpoint/api/oauth/token (308 Permanent Redirect).

Results in this error:

$ docker run -e "NODE_TLS_REJECT_UNAUTHORIZED=0" --rm node:20.19.1-alpine -e "let body = new URLSearchParams({ 'abc': 'def' }); let bodyBytes = new TextEncoder().encode(body.toString()); let req = new Request('https://self-signed-endpoint/api/oauth/token/', { method: 'POST', body: bodyBytes }); let resp = await fetch(req); console.log(resp);"
(node:1) Warning: Setting the NODE_TLS_REJECT_UNAUTHORIZED environment variable to '0' makes TLS connections and HTTPS requests insecure by disabling certificate verification.
(Use `node --trace-warnings ...` to show where the warning was created)
node:internal/deps/undici/undici:13510
      Error.captureStackTrace(err);
            ^

TypeError: fetch failed
    at node:internal/deps/undici/undici:13510:13
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async file:////[eval1]:1:225 {
  [cause]: TypeError: Cannot perform ArrayBuffer.prototype.slice on a detached ArrayBuffer
      at ArrayBuffer.slice (<anonymous>)
      at extractBody (node:internal/deps/undici/undici:5514:47)
      at safelyExtractBody (node:internal/deps/undici/undici:5621:14)
      at httpRedirectFetch (node:internal/deps/undici/undici:10842:24)
      at httpFetch (node:internal/deps/undici/undici:10785:28)
      at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
      at async node:internal/deps/undici/undici:10522:20
      at async mainFetch (node:internal/deps/undici/undici:10512:20)
}

Node.js v20.19.1

Also happens in node:latest (Node.js v24.0.2)

If I simply use .toString() like in the PR (instead of TextEncoder.encode()) I don't get the error:

$ docker run -e "NODE_TLS_REJECT_UNAUTHORIZED=0" --rm node:20.19.1-alpine -e "let body = new URLSearchParams({ 'abc': 'def' }); let req = new Request('https://self-signed-endpoint/api/oauth/token/', { method: 'POST', body: body.toString() }); let resp = await fetch(req); console.log(resp);"
(node:1) Warning: Setting the NODE_TLS_REJECT_UNAUTHORIZED environment variable to '0' makes TLS connections and HTTPS requests insecure by disabling certificate verification.
(Use `node --trace-warnings ...` to show where the warning was created)
Response {
  status: 500,
  statusText: 'Internal Server Error',
  headers: Headers {
    'access-control-allow-origin': '*',
    'content-length': '61',
    date: 'Thu, 22 May 2025 16:58:33 GMT',
    etag: '"123r53xzf551p"',
    'strict-transport-security': 'max-age=31536000; includeSubDomains;',
    'x-content-type-options': 'nosniff',
    'x-frame-options': 'SAMEORIGIN'
  },
  body: ReadableStream { locked: false, state: 'readable', supportsBYOB: true },
  bodyUsed: false,
  ok: false,
  redirected: true,
  type: 'basic',
  url: 'https://self-signed-endpoint/api/oauth/token'
}

Neither do I get the error when there's no redirect (i.e. removing the trailing / in this case):

docker run -e "NODE_TLS_REJECT_UNAUTHORIZED=0" --rm node:20.19.1-alpine -e "let body = new URLSearchParams({ 'abc': 'def' }); let bodyBytes = new TextEncoder().encode(body.toString()); let req = new Request('https://self-signed-endpoint/api/oauth/token', { method: 'POST', body: bodyBytes }); let resp = await fetch(req); console.log(resp);"
(node:1) Warning: Setting the NODE_TLS_REJECT_UNAUTHORIZED environment variable to '0' makes TLS connections and HTTPS requests insecure by disabling certificate verification.
(Use `node --trace-warnings ...` to show where the warning was created)
Response {
  status: 500,
  statusText: 'Internal Server Error',
  headers: Headers {
    'access-control-allow-origin': '*',
    'content-length': '61',
    date: 'Thu, 22 May 2025 17:03:54 GMT',
    etag: '"123r53xzf551p"',
    'strict-transport-security': 'max-age=31536000; includeSubDomains;',
    'x-content-type-options': 'nosniff',
    'x-frame-options': 'SAMEORIGIN'
  },
  body: ReadableStream { locked: false, state: 'readable', supportsBYOB: true },
  bodyUsed: false,
  ok: false,
  redirected: false,
  type: 'basic',
  url: 'https://self-signed-endpoint/api/oauth/token'
}

SamuelBoerlin avatar May 22 '25 17:05 SamuelBoerlin