nuxt-auth
nuxt-auth copied to clipboard
500 Received an instance of URLSearchParams
Environment
- Operating System:
Darwin - Node Version:
v20.11.1 - Nuxt Version:
3.12.4 - CLI Version:
3.13.0 - Nitro Version:
2.9.7 - Package Manager:
[email protected] - Builder:
- - User Config:
modules,extends,future,css,app,runtimeConfig,auth,compatibilityDate - Runtime Modules:
@nuxtjs/[email protected],@nuxt/[email protected],@vueuse/[email protected],@sidebase/[email protected] - Build Modules:
-
Reproduction
It is hard to reproduce this error without sharing the secrets. But I did a debugging with loading nuxt-auth package directly from the source. I will describe all findings.
Describe the bug
First of all I would like to share my handler file.
import { decode } from 'jsonwebtoken'
import AzureAdProvider from 'next-auth/providers/azure-ad'
import { NuxtAuthHandler } from '#auth'
async function callRefreshToken(accessToken: any) {
const url = `https://login.microsoftonline.com/${process.env.AZURE_AD_TENANT_ID}/oauth2/v2.0/token`;
const req = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body:
`grant_type=refresh_token` +
`&client_secret=${process.env.AZURE_AD_CLIENT_SECRET}` +
`&refresh_token=${accessToken.refreshToken}` +
`&client_id=${process.env.AZURE_AD_CLIENT_ID}`,
});
const res = await req.json();
return res;
}
async function refreshAccessToken(accessToken: any) {
try {
console.log('Previous token expires at', new Date(accessToken.accessTokenExpires));
console.log('Refreshing access token...');
const msToken = await callRefreshToken(accessToken);
console.log('New token received');
setAccessToken(accessToken, msToken);
console.log('Access token refreshed');
console.log('Next token expires at', new Date(accessToken.accessTokenExpires));
return accessToken;
} catch (error) {
console.error(error);
return {
...accessToken,
error: 'RefreshAccessTokenError',
};
}
}
// Persist the access_token in the encrypted JWT.
function setAccessToken(jwt: any, msToken: any) {
if (!msToken) {
return;
}
if (msToken.access_token) {
const decoded = decode(msToken.access_token)
jwt.accessToken = msToken.access_token;
if (decoded && typeof decoded !== 'string')
jwt.roles = decoded.roles;
}
if (msToken.expires_at)
jwt.accessTokenExpires = msToken.expires_at * 1000;
else if (msToken.expires_in)
jwt.accessTokenExpires = Date.now() + msToken.expires_in * 1000;
jwt.refreshToken = msToken.refresh_token;
}
export default NuxtAuthHandler({
// A secret string you define, to ensure correct encryption
secret: process.env.AUTH_APP_SECRET,
pages: {
signIn: '/auth/signIn',
signOut: '/auth/signOut',
error: '/auth/error',
verifyRequest: '/auth/verifyRequest',
newUser: '/auth/new-user'
},
callbacks: {
async jwt({ token, account, profile }) {
setAccessToken(token, account);
if (token.accessTokenExpires && Date.now() < token.accessTokenExpires) {
return token;
}
return refreshAccessToken(token);
},
async session({ session, token }) {
// Make access token available on the client.
session.roles = token.roles;
return session;
},
},
providers: [
// @ts-expect-error You need to use .default here for it to work during SSR. May be fixed via Vite at some point
AzureAdProvider.default({
clientId: process.env.AZURE_AD_CLIENT_ID,
clientSecret: process.env.AZURE_AD_CLIENT_SECRET,
tenantId: process.env.AZURE_AD_TENANT_ID,
authorization: {
params: {
scope: `openid offline_access profile api://Boss-Dev/Admin`,
},
},
})
]
})
Whenever I login or logout I receive following exception
[500] The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received an instance of URLSearchParams
error on https://github.com/sidebase/nuxt-auth/blob/c5a62d99784a64cd208a0845da0a37461337f7cc/src/runtime/server/services/authjs/nuxtAuthHandler.ts#L206
somehow, h3 package readBody throws this error, but I already test that readBody function with URLSearchParams object in a clean project and it is working ok.
The only thing that I found is
https://github.com/unjs/h3/blob/853ae882b58927bbc8eed41b9c02fa7efc529192/src/utils/body.ts#L47
this _rawBody variable is empty on simple tests in a sandbox, but when nuxt-auth sends URLSearchParams object, it is filled. In my sandbox tests, it's not hitting the next if condition and does not throw the error on
https://github.com/unjs/h3/blob/853ae882b58927bbc8eed41b9c02fa7efc529192/src/utils/body.ts#L93
Additional context
No response
Logs
ERROR [nuxt] [request error] [unhandled] [500] The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received an instance of URLSearchParams
at Function.from (node:buffer:324:9)
at ./node_modules/.pnpm/[email protected]/node_modules/h3/dist/index.mjs:410:21
at async readBody (./node_modules/.pnpm/[email protected]/node_modules/h3/dist/index.mjs:438:16)
at readBodyForNext (./node_modules/.pnpm/@[email protected][email protected][email protected][email protected]_@[email protected]_reac_wqxp7nydnxshx7noobpzq4kbim/node_modules/@sidebase/nuxt-auth/dist/runtime/server/services/authjs/nuxtAuthHandler.mjs:18:12)
at getInternalNextAuthRequestData (./node_modules/.pnpm/@[email protected][email protected][email protected][email protected]_@[email protected]_reac_wqxp7nydnxshx7noobpzq4kbim/node_modules/@sidebase/nuxt-auth/dist/runtime/server/services/authjs/nuxtAuthHandler.mjs:75:18)
at Object.handler (./node_modules/.pnpm/@[email protected][email protected][email protected][email protected]_@[email protected]_reac_wqxp7nydnxshx7noobpzq4kbim/node_modules/@sidebase/nuxt-auth/dist/runtime/server/services/authjs/nuxtAuthHandler.mjs:87:25)
at async ./node_modules/.pnpm/[email protected]/node_modules/h3/dist/index.mjs:1975:19
at async Object.callAsync (./node_modules/.pnpm/[email protected]/node_modules/unctx/dist/index.mjs:72:16)
at async toNodeHandle (./node_modules/.pnpm/[email protected]/node_modules/h3/dist/index.mjs:2266:7)
at async ufetch (./node_modules/.pnpm/[email protected]/node_modules/unenv/runtime/fetch/index.mjs:9:17)
Likely the same issue as #479. Seems to be caused in this part of useAuth.ts, in which a URLSearchParams is thrown in a fetch as request body.
https://github.com/sidebase/nuxt-auth/blob/ae2bf092f73d079a5bf479d13bf0d37db414ee23/src/runtime/composables/authjs/useAuth.ts#L121-L133
@Invisi Yes exactly, that is the initial point then server tries to parse the body and throws an exception.
I experience the same error, I am not able to log out of my application at the moment. Could you maybe treat this bug as a high prio one?
Hi @alimozdemir @Dunowen, I need to confirm why are we providing search params as body. This seemed to work so far, but apparently something changed in one of our dependencies (h3).
I will investigate today.
Is there any update on this?
After some investigation, I can confirm that this is a bug with h3 - using URLSearchParams is intended:
https://github.com/nextauthjs/next-auth/blob/285cb5ce166bdc1dc99013f0da538186c5be6e0b/packages/next-auth/src/react.tsx#L265-L280
@phoenix-ru that's why I tested h3 individually for URLSearchParams in a simple code. But it was ok, it was working
@alimozdemir Yes, I also tested it individually and URLSearchParams are definitely supported. But what happens here, I guess, is likely _fetch being dispatched to an internal function call instead of HTTP call - that's what I mean by h3 bug.
I will try to create a very minimal reproduction without nuxt-auth and see if that is the case.
I didn't know what _fetch had internal approach, although good information. Thank you for the headsup
Providing an update: I tested the Nuxt starter template and it consistently reproduces there. The only issue is that it seems to be changed in more recent versions of h3, but the starter uses 1.12.0.
Good news is that it seems to be finally patched in their main branch, we just need to wait for a new h3 release and adaptation inside Nuxt:
https://github.com/unjs/h3/blob/45cd3cca286c1309a639d1869d0de87411e79209/src/utils/body.ts#L29-L32
@phoenix-ru I am happy to hear that and hoping it will happen fast causing my app not to run in the production environment. I getting a 500 error saying Invalid URL.
I don't think it is going to be happening soon, could we have a workaround for this? e.g. disable _fetch internal approach for this call explicitly?
@phoenix-ru
main branch pointing for v2, and it is not clear yet when it is going to be ready. The last release was on Jun 20, even so, nuxt still uses 1.x version
Does anyone have a workaround? I can't run it in production environment, error 500.
main branch pointing for v2, and it is not clear yet when it is going to be ready.
You are right about it - I also considered patching this until a new h3 releases. I tried using readFormData but it unfortunately is also broken inside h3 for the same reason [1]. I will try yet again today
upd: It happens on a plain Nuxt server without our module. I will have to create a bug report to h3 and also patch it somehow on our side.
The line which causes readFormData to panic:
https://github.com/unjs/h3/blob/112fa338ef5bbde6207f3317d2ef51f0e52a59fa/src/utils/body.ts#L296
upd2: Okay, I managed to patch h3 to correctly parse URLSearchParams. I am creating a PR for them
[1]
ERROR [nuxt] [request error] [unhandled] [500] The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received an instance of URLSearchParams
at Function.from (node:buffer:319:9)
at ./node_modules/h3/dist/index.mjs:410:21
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async Object.start (./node_modules/h3/dist/index.mjs:486:26)
PR created inside h3:
https://github.com/unjs/h3/pull/888
To work I'm change this code in the build
Path: /node_modules/@sidebase/nuxt-auth/dist/runtime/server/services/authjs/nuxtAuthHandler.js
Method: createRequestForAuthjs
let body;
try {
body = isMethod(event, ["PATCH", "POST", "PUT", "DELETE"]) ? await readBody(event) : void 0;
}catch(err){}
Thank you @phoenix-ru it got fixed right now.
You have to remove your lock file and node_modules so it will fetch [email protected], then this error just gone.
But unfortunately, it gives me another error "State cookie was missing."
It is really missing in the state cookie, and it's expected somehow. I will do more research before I create another issue in the repo