Fix createOAuth2Request
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..
Can you share what version of Node.js are you using?
Can you share what version of Node.js are you using?
It's version v20.19.1 from this node:20-alpine docker image.
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'
}