Why is [email protected] pinned in the Nuxt-Auth project?
Recently, I upgraded the dependencies of a Nuxt-auth project, which resulted in a flood of warnings and abnormal login functionality. This article clearly outlines the reasons, why it couldn't be upgraded, whether it's safe now, and how I ultimately resolved it, based on my own experience.
Rendering...
# Why [email protected] Must Be Pinned in Nuxt Projects Recently, I upgraded the dependencies of a Nuxt-auth project and encountered a plethora of warnings, along with functional anomalies in the login feature. On the surface, it appeared to be a conflict between `@sidebase/nuxt-auth` and `next-auth`. However, the reality is a long-standing issue where Nuxt, Nitro, and NextAuth's package export strategies collide. This article will detail the reasons, why an upgrade is not feasible, the current security implications, and how I ultimately resolved it, based on my own experience. ## Initial Phenomena Observed After upgrading dependencies and starting the development server, Nitro's packaging phase continuously produced warnings like these: ```text WARN "next-auth/providers/google" is imported by "server/api/auth/[...].ts", but could not be resolved – treating it as an external dependency. WARN "next-auth/providers/credentials" is imported by "server/api/auth/[...].ts", but could not be resolved – treating it as an external dependency. WARN "next-auth/providers/github" is imported by "server/api/auth/[...].ts", but could not be resolved – treating it as an external dependency. WARN "next-auth/jwt" is imported by ".../@sidebase/nuxt-auth/.../nuxtAuthHandler.js", but could not be resolved – treating it as an external dependency. WARN "next-auth/core" is imported by ".../@sidebase/nuxt-auth/.../nuxtAuthHandler.js", but could not be resolved – treating it as an external dependency. ``` These warnings look like "if it can't be resolved, treat it as an external dependency." Many would dismiss them as noise. Subsequently, the login functionality became unusable: no response for username/password, and GitHub/Google callbacks failed to process. I attempted two actions that "should logically work," with the same outcome: 1. Upgrading `next-auth` to 4.22+, 4.24, or even 5. 2. Not declaring `next-auth` in the project, expecting `@sidebase/nuxt-auth` to bundle it. Both approaches led to similar WARN messages, and the login functionality remained broken. ## The First Misconception: nuxt-auth Does Not Bundle next-auth `@sidebase/nuxt-auth` is not a complete authentication kernel; it's a Nuxt adaptation layer. The actual signing of JWTs, running OAuth, and providing Google/GitHub/Credentials is handled by `next-auth`. You can confirm this by looking at its `package.json`: ```json { "peerDependencies": { "next-auth": "~4.21.1" }, "peerDependenciesMeta": { "next-auth": { "optional": true } } } ``` There are three key points: - It's a **peer** dependency, not a bundled dependency. The module's source code imports `"next-auth/core"`, and at runtime, it expects to find this package in your project. - The version is `~4.21.1`, allowing only 4.21.x, not `^4`. - It's also marked as **optional**. This is because the module has a `local` provider that doesn't require Auth.js. If you choose the `authjs` option, "optional" does not mean "you can skip installing it." The official Quick Start guide is quite explicit: besides installing via npm, you must install it again, specifying the version: ```bash pnpm i [email protected] ``` The documentation also warns that due to NextAuth's breaking changes, **nuxt-auth is only compatible with versions below 4.23.0**, and pinning to `4.21.1` is recommended. Therefore, relying on the module's bundled version will almost certainly fail in a pnpm project. pnpm enforces strict isolation: if you don't list `next-auth` in your `dependencies`, it won't exist in the root `node_modules`. When Nitro tries to resolve `next-auth/providers/google`, it can't find it and marks it as external. At runtime, when Node attempts to `import` it, the package is missing, and the login flow breaks. npm sometimes hoists peer dependencies to the root, making it appear as if it "works without declaration." Switching to pnpm or yarn will reveal the underlying issue. This is not a package manager bug but the expected behavior of peer dependencies combined with optionality and strict isolation. ## What Exactly Do Those WARNs Mean? This isn't ESLint; it's **Nitro/Vite/Rollup failing to resolve during server-side packaging.** Two import chains are simultaneously looking for subpaths within `next-auth`: - Your catch-all route: `next-auth/providers/google`, `github`, `credentials` - The module's internal `NuxtAuthHandler`: `next-auth/core`, `next-auth/jwt` If resolution succeeds, these modules are bundled into the Nitro output. If it fails, the packager states, "treat as external dependency"—meaning: ignore it during build, and let Node find it in `node_modules` at runtime. This leads to a confusing experience: the build "succeeds" with yellow text; however, when the worker starts or login is attempted, you get `Cannot find package 'next-auth'` or `Package subpath './core' is not defined by "exports"`. The WARN is a symptom. The root cause is: **the subpath either doesn't exist in the currently installed `next-auth` version or is hidden by `exports`.** ## Why Specifically 4.21.1? `@sidebase/nuxt-auth`'s handler directly references NextAuth's then-public internal entry points: `next-auth/core` and `next-auth/jwt`. `4.21.1` still treats these subpaths as importable modules. Starting from **4.23.0**, NextAuth tightened its `package.json` `exports`, removing internal paths like `./core` from public exports in preparation for Auth.js / v5. When Nitro attempts to resolve them using the old method, it results in: ```text Package subpath './core' is not defined by "exports" ``` Sidebase's own issue tracker confirms this: [NextAuth >= v4.23.0 Breaking change](https://github.com/sidebase/nuxt-auth/issues/514). The maintainers later admitted that they would need to refactor the module according to the new architecture, after which it would no longer be compatible with versions below 4.23. However, this refactoring for v1.x has not yet been released as a stable solution. Consequently, v1.x continues to pin its peer dependency to `~4.21.1`. `next-auth@5` is even more radical: the package structure, provider paths, and handler APIs have all changed. `@sidebase/[email protected]` cannot connect to it. Another easily overlooked detail: the providers in 4.21.1 are CommonJS. Nuxt projects are typically ESM (`"type": "module"`), which is why the official examples require `.default()`: ```ts import { NuxtAuthHandler } from "#auth" import GithubProvider from "next-auth/providers/github" import CredentialsProvider from "next-auth/providers/credentials" export default NuxtAuthHandler({ secret: process.env.NUXT_AUTH_SECRET, providers: [ // @ts-expect-error When loading CJS providers in ESM, you need to use .default GithubProvider.default({ clientId: process.env.GITHUB_CLIENT_ID, clientSecret: process.env.GITHUB_CLIENT_SECRET, }), // @ts-expect-error CredentialsProvider.default({ name: "Credentials", credentials: { email: { label: "Email", type: "text" }, password: { label: "Password", type: "password" }, }, async authorize(credentials) { // Perform your own validation here, return user or null return null }, }), ], session: { strategy: "jwt" }, pages: { signIn: "/login", }, }) ``` This isn't a project quirk but interoperability when CJS providers are used in an ESM server environment. Once versions prioritize ESM, `.default` often becomes `undefined`, the provider fails to register, and login silently fails in a different way. The corresponding Nuxt configuration simply needs to declare the use of Auth.js: ```ts export default defineNuxtConfig({ modules: ["@sidebase/nuxt-auth"], auth: { originEnvKey: "NUXT_AUTH_BASE_URL", provider: { type: "authjs", trustHost: false, addDefaultCallbackUrl: true, }, }, }) ``` It's recommended to keep `trustHost: false`. In production, configure `NUXT_AUTH_BASE_URL` to the full URL, e.g., `https://example.com/api/auth`, not just the domain root. ## Why Two Incorrect Operations Both Fail **Not declaring `next-auth`.** pnpm will not hoist optional peer dependencies to your application's root dependencies. Both your `server/api/auth/[...].ts` and the internal `nuxtAuthHandler.js` will fail to resolve. This is the same type of issue as in sidebase's [#748](https://github.com/sidebase/nuxt-auth/issues/748) and [#877](https://github.com/sidebase/nuxt-auth/issues/877). **Switching to 4.22+ / 4.24 / 5.** The package installs, but `exports` no longer exposes `./core`. Nitro still fails to resolve, and at runtime, you get `ERR_PACKAGE_PATH_NOT_EXPORTED`. Login remains unusable. Scanners might temporarily quiet down, but the application breaks. Do not list it as `^4.21.1` in `package.json`. `^` will include 4.22, 4.23, and 4.24. Use a tilde or an exact version: ```json { "dependencies": { "@sidebase/nuxt-auth": "1.3.1", "next-auth": "~4.21.1" } } ``` After installation, confirm the actual version with your package manager to avoid silently installing another version in the lockfile: ```bash pnpm ls next-auth ``` You should see `[email protected]`. Then, re-run `nuxt prepare` or restart the dev server. The "treating it as an external dependency" warnings should disappear, and the login route can be correctly bundled into Nitro. ## Are There Newer Versions That Can Be Upgraded Together? As of the writing of this article (September 2026), the situation is as follows: | Item | Status | | ---------------------------- | --------------------------------- | | `@sidebase/nuxt-auth` Stable | `1.3.1` (2026-06-30), latest | | Peer Dependency | Still `next-auth@~4.21.1` | | Sidebase 2.0 | On roadmap, not on npm | | Official `@auth/nuxt` | Auth.js docs still show Open PRs | 1.3.x fixes issues within the module itself, such as refresh, cookies, and Nuxt 4 compatibility, but **does not relax the `next-auth` version lock**. Upgrading nuxt-auth from 1.1 to 1.3 will not resolve the WARNs discussed in this article. Sidebase originally planned to switch to Auth.js v5 in version 2.0. Discussions can be found in [Roadmap #1028](https://github.com/sidebase/nuxt-auth/issues/1028) and [Migration #673](https://github.com/sidebase/nuxt-auth/issues/673). They have undergone at least two rounds of migration PRs, but all were halted due to the instability of `@auth/core` / `oauth4webapi`. The maintainers have publicly stated their concern about the stability of Auth.js at the time and were unwilling to release a stable version with unstable dependencies. Later, Auth.js merged into Better Auth. Some suggested sidebase migrate directly, but the maintainers refused: Auth.js can handle JWT sessions without a database, while Better Auth defaults to database sessions, which is more than just a package name change. The current Better Auth wrappers in the Nuxt community are also still in their early stages. Therefore, there is no "just change the version number and keep the business login code unchanged" simultaneous upgrade path today. Moving away from 4.21.1 essentially means switching the authentication kernel: waiting for sidebase 2.0, or migrating to `nuxt-auth-utils` / Better Auth yourself, which would require rewriting the handler, `signIn` function, session reading, and cookie handling. This is a project migration, not a dependency upgrade. ## Is Pinning to 4.21.1 Secure? This was my primary concern before deciding "not to touch it." The answer needs to be broken down and not swayed by the red text from `npm audit`. Scanners will almost certainly report: - `next-auth < 4.24.5`: [CVE-2023-48309](https://github.com/advisories/GHSA-v64w-49xw-qq89), empty user spoofing - `next-auth < 4.24.15`: [CVE-2026-73419](https://github.com/advisories/GHSA-x445-f3h2-j279), OAuth provider confusion Both patches are in the 4.24.x versions, but 4.24.x breaks the current nuxt-auth login. So the question becomes: **Are these vulnerabilities exploitable via the Nuxt path?** **CVE-2023-48309 is largely inapplicable to NuxtAuth.** It only affects Next.js's default `withAuth` middleware—the kind that only checks "is there a session?" Attackers could use an incomplete JWT to appear logged in, but without email or permissions. The sidebase documentation specifically addresses this section: they do not use Next's middleware but rather Nuxt/h3's session utilities. The maintainers marked this issue as not affecting the module in [#1001](https://github.com/sidebase/nuxt-auth/issues/1001). `next@13` may also appear in lock files. This is a peer dependency of `[email protected]`, not your web server. Nuxt applications do not run Next Server Actions, so Next SSRF reported by scanners is typically not an attack surface for this site. **CVE-2026-73419 hits the version, but the full exploitation conditions are stringent.** 4.21.1 indeed falls within the affected range. The official description requires simultaneously: multiple OAuth providers, **while logged in**, linking a second provider to the current user, and at least one provider's callback can bypass PKCE. If you only initiate OAuth on the login page, do not have an entry point to "link a second account after logging in," and do not use NextAuth Adapter's `linkAccount` functionality, the actual risk is much lower than described by the scanner. This is a theoretical risk stemming from the library's age, not a "must stop immediately" situation. My conclusion for myself is: - **You can continue using** `@sidebase/[email protected]` + `[email protected]` - Treat audit warnings as known, assessed, and temporarily unfixed. - Do not upgrade `next-auth` just to make the scanner green. What you truly need to secure are your usage patterns, not just the version number: - Use a sufficiently long random string for `NUXT_AUTH_SECRET`; it should fail to start without a key. - Keep `trustHost` as `false`; do not blindly trust Host headers from forwarded requests. - Set `NUXT_AUTH_BASE_URL` to an absolute address including `/api/auth`. - Frontend middleware should only handle redirection to the login page; API authorization must be re-verified on the server by checking the session, and at minimum, confirm the existence of user identity fields. Management APIs also require role validation. - Use HTTPS in production; only then can session cookies work with Auth.js's secure prefix. You must also accept a structural limitation: `next-auth` v4 is in maintenance mode. If new vulnerabilities targeting the **core JWT validation or OAuth callback** emerge, and sidebase continues to pin 4.21.1, it won't be fixable with `pnpm update`; you'll need to switch stacks. It's more honest to add this to your technical debt list than to pretend the scanner is green. ## The Solution I Ultimately Adopted The solution is actually very brief, so brief it contradicts the instinct to "upgrade dependencies." 1. Explicitly add `next-auth@~4.21.1` to your application's `dependencies` to align with `@sidebase/nuxt-auth`'s peer dependency. 2. When using pnpm, do not assume peer dependencies will be automatically hoisted. 3. In the catch-all route, import providers as per the official method, using `.default()` for CJS interoperability. 4. Do not change `next-auth` to `^4` or `5`, and do not remove it. 5. Record the two `next-auth` CVEs from `npm audit` as "assessed for Nuxt path, cannot upgrade with patch versions." 6. Wait for sidebase 2.0 or prepare for a dedicated authentication migration project, rather than bundling it with routine dependency upgrades. Verification is straightforward: after restarting the dev server, Nitro should no longer treat `next-auth/core`, `next-auth/jwt`, or `next-auth/providers/*` as external. Username/password and OAuth callbacks should complete successfully. `pnpm ls next-auth` should only show 4.21.1. ## If You Absolutely Must Move Away from 4.21.1 in the Future There is no third "just upgrade the version" path. The options I've left for myself are: 1. **Continue pinning.** The module is still maintained, and it works with Nuxt 4. The cost is remaining on NextAuth v4 indefinitely. 2. **Wait for sidebase 2.0.** The official team has stated they will migrate to Auth.js v5, but there is no stable timeline to follow, nor a stable npm dist-tag available. 3. **Switch stacks and rewrite.** The Nuxt official recipe direction is `nuxt-auth-utils` (encrypted cookie session, OAuth requires self-handling); or Better Auth. Both require rewriting the login entry point, session shape, and callback URLs, which will cause existing login sessions to drop. Until then, I will treat this version pairing as a frozen layer of the login infrastructure: I can fix my own pages and authorization logic, but I will not "tidy up" `next-auth`. ## Conclusion The awkwardness of this situation stems from violating dependency management intuition: scanners say it's old, documentation says to pin it, the build only warns without failing, and failures occur during login. Separating these three aspects clarifies the picture: - **Resolution failures** arise from pnpm's isolation and NextAuth no longer exporting `./core` from 4.23 onwards. - **Inability to upgrade** is because `@sidebase/[email protected]` still relies on those internal entry points, and v2.0 has not been delivered. - **Ability to continue using it** is because the most frequently scanned CVE targets Next.js's default middleware, not NuxtAuth's own session tools; the newer OAuth mix-up requires you to have a "link second provider after login" path. Therefore, I've formulated the solution as a self-executable directive: on Nuxt's `authjs` provider, pin `[email protected]` as a first-class dependency, treat audit red flags as known debt, and reserve the actual upgrade for the next authentication migration.
Comments
Please login to view and post comments
Go to Login