なぜ Nuxt-Auth プロジェクトでは [email protected] を固定しなければならないのですか?
最近、Nuxt-authプロジェクトの依存関係をアップグレードしたところ、大量の警告が発生し、ログイン機能にも異常が出ました。この記事では、その原因、なぜアップグレードできないのか、現在安全なのかそうでないのか、そして最終的に私がどう対処したのかを、自分が経験した道のりに沿って明確に記述します。
レンダリング中...
# Nuxt プロジェクトで [email protected] を固定する必要がある理由 最近、Nuxt-auth プロジェクトの依存関係をアップグレードしたところ、大量の警告が発生し、ログイン機能も異常をきたしました。表面的には `@sidebase/nuxt-auth` と `next-auth` の依存関係の競合問題に見えますが、実際には Nuxt、Nitro、NextAuth のパッケージエクスポート戦略が衝突する古くからの問題です。この記事では、その原因、なぜアップグレードできないのか、現在安全なのか、そして私が最終的にどのように対処したのかを、私が経験した道のりに沿って明確に記述します。 ## まず見られた現象 依存関係をアップグレードして開発サービスを起動すると、Nitro のバンドル段階で次のような警告が連続して表示されます。 ```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. ``` 警告は「解決できないなら外部依存として扱う」というように見えます。多くの人はこれをノイズとして無視するでしょう。しかし、その後ログインが利用できなくなります。アカウントとパスワードは反応せず、GitHub / Google のコールバックも機能しません。 私は「常識的に行うべき」2つのことを試しましたが、結果は同じでした。 1. `next-auth` を 4.22+、4.24、さらには 5 にアップグレードする。 2. プロジェクト内で `next-auth` を宣言せず、`@sidebase/nuxt-auth` が独自に含んでいることを期待する。 どちらの道も同様の WARN に戻り、ログインはやはり失敗します。 ## 最初の誤解:nuxt-auth は next-auth を内蔵していない `@sidebase/nuxt-auth` は完全な認証カーネルではなく、Nuxt のアダプテーションレイヤーです。`authjs` の経路で実際に JWT を発行し、OAuth を実行し、Google / GitHub / Credentials を提供するのは `next-auth` です。 その `package.json` を見れば確認できます。 ```json { "peerDependencies": { "next-auth": "~4.21.1" }, "peerDependenciesMeta": { "next-auth": { "optional": true } } } ``` 要点は3つあります。 - これは **peer** であり、bundled dependency ではありません。モジュールのソースコードで `import "next-auth/core"` と記述されている場合、実行時にはあなたのプロジェクト内でこのパッケージを探しに行きます。 - バージョンは `~4.21.1` であり、4.21.x のみ許可され、`^4` ではありません。 - さらに **optional** とマークされています。これは、モジュールには `local` プロバイダーもあり、Auth.js に接続しないことも可能だからです。しかし、`authjs` を選択した場合、optional は「インストールしなくてもよい」という意味ではありません。 公式の Quick Start には非常に明確に書かれています。npm 以外に、自分で再度インストールする必要があり、バージョンも指定されています。 ```bash pnpm i [email protected] ``` ドキュメントは同時に警告しています。NextAuth の破壊的変更により、**nuxt-auth は 4.23.0 未満としか互換性がありません**。`4.21.1` に固定することを推奨しています。 したがって、「モジュールに付属しているものを使用する」という方法は、pnpm プロジェクトではほぼ確実に失敗します。pnpm は厳密な分離を行います。`next-auth` を自身の `dependencies` に記述しない限り、ルートディレクトリの `node_modules` にはそれが存在しません。Nitro が `next-auth/providers/google` を解決しようとしても見つからず、external としてマークするしかありません。実行時に Node が `import` しようとしても、パッケージが存在しないため、ログインフローは直接中断されます。 npm は時々 peer をルートに持ち上げることがあり、「宣言しなくても動作する」ように見えることがあります。pnpm / yarn に切り替えると、その実態が明らかになります。これはパッケージマネージャーのバグではなく、peer + optional + 厳密な分離が重なった場合の予期される動作です。 ## その WARN の連鎖は何を意味しているのか これは ESLint ではなく、**Nitro / Vite / Rollup がサーバーサイドパッケージをバンドルする際に解決に失敗している**ことを示しています。 2つのインポートチェーンが同時に `next-auth` のサブパスを探しています。 - あなたの catch-all ルート:`next-auth/providers/google`、`github`、`credentials` - モジュール内部の `NuxtAuthHandler`:`next-auth/core`、`next-auth/jwt` 解決に成功すれば、これらのモジュールは Nitro の成果物に含まれます。解決に失敗すると、バンドラーは「外部依存として処理する」と言います。これは、ビルド時には関与せず、実行時に Node が `node_modules` 内で探すようにするという意味です。 その結果、非常に混乱する体験が生じます。ビルドは「成功」し、黄色い文字が表示されるだけですが、ワーカーが起動したり、ログインしようとすると、`Cannot find package 'next-auth'` または `Package subpath './core' is not defined by "exports"` に変わります。 WARN は症状です。根本原因は、**現在インストールされている `next-auth` のサブパスが、存在しないか、`exports` によって隠されているかのどちらかである**ことです。 ## なぜ 4.21.1 なのか `@sidebase/nuxt-auth` のハンドラーは、NextAuth が当時まだ公開していた内部エントリポイントである `next-auth/core` と `next-auth/jwt` を直接参照していました。`4.21.1` はこれらのサブパスをインポート可能なモジュールとして扱っていました。 **4.23.0** 以降、NextAuth は `package.json` の `exports` を厳格化し、`./core` のような内部パスを公開エクスポートから除外しました。これは、後の Auth.js / v5 への準備のためです。Nitro が以前の方法で解決しようとすると、次のようになります。 ```text Package subpath './core' is not defined by "exports" ``` sidebase 自身の issue でこの件は確定しています:[NextAuth >= v4.23.0 Breaking change](https://github.com/sidebase/nuxt-auth/issues/514)。メンテナーも後に認めています。彼らは新しいアーキテクチャに合わせてモジュールを一度変更する必要があり、変更後は 4.23 未満との互換性がなくなります。しかし、1.x のこの変更はまだ安定版としてリリースされていません。そのため、1.x は引き続き peer を `~4.21.1` に固定しています。 `next-auth@5` はさらに徹底しており、パッケージ構造、プロバイダーパス、ハンドラー API のすべてが変更されました。`@sidebase/[email protected]` はこれに対応できません。 もう一つ見落としがちな詳細があります。4.21.1 のプロバイダーは CommonJS です。Nuxt プロジェクトは通常 ESM (`"type": "module"`) であるため、公式の例では `.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 ESM が CJS プロバイダーをロードする際に .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) { // ここで独自の検証を行い、ユーザーまたは null を返します return null }, }), ], session: { strategy: "jwt" }, pages: { signIn: "/login", }, }) ``` これはプロジェクトの癖ではなく、CJS プロバイダーが ESM サーバーサイドで動作する際の相互運用性です。バージョンが ESM 優先になると、`.default` は `undefined` になることが多く、プロバイダーがまったく登録されず、ログインは別の方法でサイレントに失敗します。 対応する Nuxt の設定では、Auth.js を使用することを宣言するだけで済みます。 ```ts export default defineNuxtConfig({ modules: ["@sidebase/nuxt-auth"], auth: { originEnvKey: "NUXT_AUTH_BASE_URL", provider: { type: "authjs", trustHost: false, addDefaultCallbackUrl: true, }, }, }) ``` `trustHost: false` は維持することを推奨します。本番環境では `NUXT_AUTH_BASE_URL` を `https://example.com/api/auth` のような完全なアドレスに設定し、ドメインのルートパスだけを記述しないようにしてください。 ## 2つの誤った操作がなぜ両方とも失敗するのか **`next-auth` を宣言しない。** pnpm は optional peer をあなたのアプリケーションのルート依存関係に昇格させません。あなたの `server/api/auth/[...].ts` とモジュール内部の `nuxtAuthHandler.js` が同時に解決に失敗します。これは sidebase の [#748](https://github.com/sidebase/nuxt-auth/issues/748)、[#877](https://github.com/sidebase/nuxt-auth/issues/877) と同じ種類の問題です。 **4.22+ / 4.24 / 5 に変更する。** パッケージはインストールできますが、`exports` が `./core` をエクスポートしなくなります。Nitro も同様に解決に失敗し、実行時に `ERR_PACKAGE_PATH_NOT_EXPORTED` となります。ログインはやはり利用できません。スキャナーは一時的に静まるかもしれませんが、アプリケーションは壊れてしまいます。 `package.json` に `^4.21.1` と記述しないでください。`^` は 4.22、4.23、4.24 に進んでしまいます。正しいのはチルダまたは正確なバージョンです。 ```json { "dependencies": { "@sidebase/nuxt-auth": "1.3.1", "next-auth": "~4.21.1" } } ``` インストール後、パッケージマネージャーで実際のバージョンを確認し、lockfile に別のバージョンがこっそり追加されていないことを確認してください。 ```bash pnpm ls next-auth ``` 表示されるのは `[email protected]` であるはずです。その後、`nuxt prepare` を再実行するか、dev サーバーを再起動すると、「treating it as an external dependency」という警告の連鎖は消え、ログインルートが実際に Nitro にバンドルされるようになります。 ## 新しいバージョンに一緒にアップグレードできるのか この記事を執筆している時点(2026年9月)での状況は次のとおりです。 | 項目 | 状況 | | ---------------------------- | ------------------------------- | | `@sidebase/nuxt-auth` 安定版 | `1.3.1`(2026-06-30)、最新 | | peer | 引き続き `next-auth@~4.21.1` | | sidebase 2.0 | ロードマップにはあるが、npm にはない | | 公式 `@auth/nuxt` | Auth.js ドキュメントではまだ Open PR と表記 | 1.3.x で修正されたのは、モジュール自身の更新、cookie、Nuxt 4 との互換性であり、`next-auth` のバージョンロックは**解除されません**。nuxt-auth を 1.1 から 1.3 にアップグレードしても、この記事で述べられている WARN は解決しません。 sidebase は当初、2.0 で Auth.js v5 に移行する予定で、関連する議論は [Roadmap #1028](https://github.com/sidebase/nuxt-auth/issues/1028) と [Migration #673](https://github.com/sidebase/nuxt-auth/issues/673) で行われています。彼らは少なくとも2回の移行 PR を作成しましたが、いずれも `@auth/core` / `oauth4webapi` の不安定さが原因で停止しました。メンテナーは公に、当時の Auth.js の安定性に不安があり、不安定な依存関係を抱えたまま安定版をリリースしたくないと述べていました。 その後、Auth.js は Better Auth に統合されました。sidebase に直接移行するよう提案する人もいましたが、メンテナーは拒否しました。Auth.js はデータベースなしで JWT セッションを使用できますが、Better Auth はデフォルトでデータベースセッションを必要とするため、単にパッケージ名を変更するだけでは済まないからです。Nuxt コミュニティにおける現在の Better Auth のラッパーもまだ初期段階にあります。 したがって、今日、「バージョン番号だけを変更し、ビジネスロジックのログインコードは変更しない」という同期アップグレードは存在しません。4.21.1 から離れたい場合、本質的には認証カーネルを切り替えることになります。sidebase 2.0 を待つか、`nuxt-auth-utils` / Better Auth に自分で移行し、ハンドラー、`signIn`、セッションの読み取り、cookie を書き直す必要があります。それは依存関係のアップグレードではなく、プロジェクトの移行です。 ## 4.21.1 に固定したままで安全なのか これは私が「まずは何もしない」と決める前に最も気になった問題です。答えは分解して考える必要があり、`npm audit` の赤い文字に惑わされてはいけません。 スキャナーはほぼ確実に次のことを報告します。 - `next-auth < 4.24.5`:[CVE-2023-48309](https://github.com/advisories/GHSA-v64w-49xw-qq89)、空のユーザーの偽造 - `next-auth < 4.24.15`:[CVE-2026-73419](https://github.com/advisories/GHSA-x445-f3h2-j279)、OAuth プロバイダーの混同 どちらのパッチも 4.24.x にありますが、4.24.x では現在の nuxt-auth のログインが機能しなくなります。したがって、問題は次のようになります。**これらの脆弱性は Nuxt のパスに影響を与えるのか。** **CVE-2023-48309 は NuxtAuth には基本的に適用されません。** これは Next.js のデフォルトの `withAuth` ミドルウェア、つまり「セッションがあるかどうか」だけをチェックする記述方法にのみ影響します。攻撃者は不完全な JWT を使用してログインしているように見せかけることができますが、email や権限はありません。sidebase のドキュメントにはこのセクションが特別に記述されており、彼らは Next のミドルウェアを使用せず、Nuxt / h3 のセッションツールを独自に使用しています。メンテナーは [#1001](https://github.com/sidebase/nuxt-auth/issues/1001) でこの問題がモジュールに影響しないとマークしています。 ロックファイルには `next@13` が表示されることもよくあります。それは `[email protected]` の peer であり、あなたの Web サーバーではありません。Nuxt アプリケーションは Next Server Actions を実行しないため、スキャナーが報告する Next SSRF は通常、このサイトの HTTP 攻撃面ではありません。 **CVE-2026-73419 はバージョンに該当しますが、完全な悪用条件は非常に厳格です。** 4.21.1 は確かに影響を受ける範囲に含まれます。公式の説明では、複数の OAuth プロバイダー、**ログイン状態での**2番目のプロバイダーの現在のユーザーへのバインド、そして少なくとも1つのプロバイダーのコールバックが PKCE なしで通過できることが同時に必要です。ログインページからのみ OAuth を開始し、「ログイン後に2番目のアカウントをバインドする」エントリポイントがなく、NextAuth Adapter の `linkAccount` の仕組みも使用していない場合、実際のリスクはスキャナーが示すよりもはるかに小さいです。これはライブラリの古さによる理論上のリスクであり、「直ちに停止しなければならない」というものではありません。 私自身の結論は次のとおりです。 - `@sidebase/[email protected]` + `[email protected]` は**引き続き使用可能** - audit 警告は既知、評価済み、一時的に修正しないものとして扱う - 赤い文字を消すために `next-auth` をアップグレードしない 本当に守るべきは使い方であり、バージョン番号そのものではありません。 - `NUXT_AUTH_SECRET` には十分な長さのランダムな文字列を使用し、起動時にキーがない場合は失敗させるべきです。 - `trustHost` は `false` を維持し、転送ヘッダーの Host を安易に信用しないこと。 - `NUXT_AUTH_BASE_URL` は `/api/auth` を含む絶対アドレスとして記述すること。 - フロントエンドのミドルウェアはログインページへのリダイレクトのみを担当し、API 認証はサーバーサイドで再度セッションをチェックし、「オブジェクトが存在するかどうか」だけでなく、少なくともユーザーIDフィールドが存在することを確認し、管理インターフェースではロールも検証すること。 - 本番環境では HTTPS を使用し、セッションクッキーが Auth.js のセキュリティプレフィックスに従って機能するようにすること。 また、構造的な制約も受け入れる必要があります。`next-auth` v4 はメンテナンスモードに入っています。今後、**JWT 検証または OAuth コールバックのコア**に影響を与える新しい脆弱性が見つかり、sidebase がまだ 4.21.1 に固定されている場合、それは `pnpm update` で解決できる問題ではなく、スタックを切り替えるしかありません。このことを技術的負債リストに記録しておく方が、スキャナーが緑になったと偽るよりも正直です。 ## 私が最終的に採用した解決策 解決策は実は非常に短く、「依存関係のアップグレード」という本能とは逆のものです。 1. アプリケーション自身の `dependencies` に `next-auth@~4.21.1` を明示的に追加し、`@sidebase/nuxt-auth` の peer と合わせる。 2. パッケージマネージャーに pnpm を使用する場合、peer が自動的に昇格されると仮定しない。 3. catch-all ルートは公式の方法でプロバイダーをインポートし、CJS は `.default()` を経由する。 4. `next-auth` を `^4` や `5` に変更せず、削除もしない。 5. `npm audit` の next-auth CVE 2件については、「Nuxt パスは評価済みであり、パッチバージョンでのアップグレードは不可能」として記録する。 6. sidebase 2.0 を待つか、認証移行の準備ができたときに、別途プロジェクトを立ち上げ、日常の依存関係アップグレードとは切り離して行う。 検証も非常に直接的です。dev サーバーを再起動した後、Nitro は `next-auth/core`、`next-auth/jwt`、`next-auth/providers/*` を外部依存として扱うべきではありません。アカウントとパスワード、OAuth コールバックが完了すること。`pnpm ls next-auth` が 4.21.1 のみを表示すること。 ## 将来的に 4.21.1 から離れる必要がある場合 「バージョンだけを上げる」という第三の道はありません。私が自分に残した選択肢は次のとおりです。 1. **固定し続ける。** モジュールはまだメンテナンスされており、Nuxt 4 で使用できます。代償は、NextAuth v4 に永遠に留まることです。 2. **sidebase 2.0 を待つ。** 公式は Auth.js v5 への移行を表明していますが、追跡可能な安定したタイムラインはなく、本番環境で使用できる npm dist-tag もありません。 3. **スタックを切り替えて書き直す。** Nuxt の公式レシピの方向性は `nuxt-auth-utils`(暗号化された cookie セッション、OAuth は自分で接続する必要がある)または Better Auth です。どちらもログインエントリポイント、セッションの形式、コールバック URL を書き直す必要があり、既存のログイン状態は失われます。 それまでは、このバージョンの組み合わせをログインインフラストラクチャのフリーズレイヤーとして扱います。自分のページや認証ロジックは修正できますが、`next-auth` を「整理」しようとはしません。 ## まとめ この件が厄介なのは、依存関係管理の直感に反するからです。スキャナーは古いと言い、ドキュメントは固定しろと言い、ビルドは警告するだけで失敗せず、失敗はログイン時に発生します。この3つのことを切り離すと、スムーズになります。 - **解決失敗**は pnpm の分離と、NextAuth 4.23 以降が `./core` をエクスポートしなくなったことに起因します。 - **アップグレードできない**のは、`@sidebase/[email protected]` がまだその内部エントリポイントに依存しており、2.0 がリリースされていないからです。 - **引き続き使用できる**のは、最も頻繁にスキャンされる CVE が Next.js のデフォルトミドルウェアを対象としており、NuxtAuth 自身のセッションツールではないためです。新しい OAuth mix-up は、「ログイン後に2番目のプロバイダーをバインドする」パスがあるかどうかによります。 したがって、私は解決策を、自分で実行できる一文にまとめました。Nuxt の `authjs` プロバイダーにおいて、`[email protected]` を第一級の依存関係として固定し、audit の赤い文字は既知の負債として扱い、真のアップグレードは次回の認証移行まで残しておく、と。
END
コメント
ログインしてコメントを閲覧・投稿してください
ログインへ