Nuxt4 + Amplify(Cognito)で起きた「Missing bearer token」問題と SSR 認証のリアル
この記事では、Nuxt4 と AWS Amplify(Cognito 認証)を組み合わせた掲示板アプリ開発の中で起きた、 「ユーザー一覧ページが初期表示で Missing bearer token エラーになる」 という問題と、その修正内容・背景技術について紹介します。
同じ構成でアプリを作っている方や、Nuxt + Cognito の SSR まわりでハマる方の参考になれば幸いです。
🔧 プルリクの概要
問題が発生したのは 管理者向け「ユーザー一覧ページ」です。
ページの初期表示時に、内部で /api/users を叩いてユーザー一覧を取得します。しかし SSR(サーバーサイドレンダリング)により、以下のコードがサーバー側で実行されました。
await useAsyncData('admin-users-list', () => $fetch('/api/users'))
ところが Cognito 認証が必要な /api/users では
Authorization ヘッダーに Bearer Token が必要。
しかし SSR のタイミングでは Cognito のトークンがまだ存在せず、結果として:
Missing bearer token
というエラーが発生。
このプルリクは、初期表示時に Missing bearer token が出る問題を修正するプルリクです。
🎯 修正内容:SSR をやめて "クライアント側で" 認証後に fetch する方式に変更
最終的には、API の呼び出しタイミングを SSR → クライアント側へ移動することで解決しました。
修正後の流れは次のとおり:
onMounted(async () => {
start() // 認証情報の準備
await sync() // LocalStorage の Cognito トークンを同期
await loadUsers() // 認証ヘッダー付きで /api/users を fetch
})
要点は以下。
- SSR(サーバー側)では Cognito トークンを取得できない
sync()によりクライアント側でトークンが揃うbuildHeaders()が正しい Authorization ヘッダを生成$fetch('/api/users')が正常に実行されるようになる
結果、Missing bearer token は完全に解消されました。
🔍 なぜ SSR で Missing bearer token が出たのか?
✔ Cognito は LocalStorage 管理の SPA 認証方式
Amplify(Cognito)の認証情報はこう管理されています:
- accessToken → LocalStorage
- idToken → LocalStorage
- refreshToken → LocalStorage
👉 SSR(サーバー側)では LocalStorage が存在しないため、トークンを取得できない
✔ SSR の useAsyncData() は “サーバー側で” 一度実行される
そのため…
- まだ認証同期が済んでいない
- LocalStorage にアクセスできない
- 認証ヘッダーに必要な Bearer Token を組み立てられない
という状況になります。
つまり構造的に、
という問題が根本原因です。
🤔 そもそも、Cognito で SSR 認証を行わないのは一般的なのか?
結論:はい、一般的です。むしろ Cognito は SSR 認証が苦手なサービスです。
理由は明確で、
- Cognito 認証は SPA / モバイル向け
- 認証情報はブラウザに保存される(LocalStorage)
- SSR(サーバー)からセッションを参照できない
- HttpOnly Cookie による SSR 認証方式を前提としていない
この構造は、SPA 向けには最高に便利ですが、 SSR アプリケーションには向かない側面があります。
📈 一方で、SSR 認証が必要になるユースケースもある
たとえば:
① URLを直接開いたときも SSR で認証を反映したい管理画面
初期描画からユーザー一覧やダッシュボードを表示したい場合。
② SEO が必要なページ+ログイン状態による表示差分
ブログの下書きプレビューなど。
③ SSR でサーバー側 API にユーザー認証情報を渡したいケース
「SSR → API → DB」のようにサーバー中心で処理する場合。
こういった “SSR の時点でログイン状態を使いたい” ユースケースでは、 Cognito の LocalStorage トークン方式では限界があるのです。
🔥 Auth0 を使うとどうなる? → SSR 認証が実現できる
Auth0 には HttpOnly Cookie 方式の SSR 認証が組めます。
具体的には:
- Universal Login でログイン
- サーバー側(Nuxt)でコード交換
access_tokenを HttpOnly Cookie に保存- SSR 時に Cookie を読み取り、認証情報を復元
- SSR の useAsyncData() でも認証済み fetch が可能
つまり、今回の PR のような「SSR で Missing bearer token」が Auth0 なら根本的に発生しない構造が作れます。
📝 まとめ:Nuxt × Cognito の SSR は構造的に難しい
今回発生した「Missing bearer token エラー」は、 Cognito が SSR 認証に向かないことによる構造的な問題でした。
✅ 課題
SSR の useAsyncData() がサーバー側で実行される
↓
サーバー側では Cognito トークンがない
↓
Authorization ヘッダを生成できない
↓
Missing bearer token が発生
✅ 解決
クライアント側 onMounted() で認証同期後に fetch
→ 認証ヘッダーも正しく付与され正常に動作
✅ 学び
- Cognito の LocalStorage トークン方式は SSR と相性が悪い
- SSR 認証が必要なアプリなら、Cookie セッション型(Auth0など)が向く
- Nuxt / Amplify 構成では「認証 API は CSR で呼ぶ」が王道
🎉 おわりに
今回の修正は一見小さな変更ですが、背景には Cognito の認証方式と SSR の本質的な相性問題 という深い技術的テーマがありました。
Nuxt + Amplify で SSR を使う方や、Cognito の制約に悩む方の参考になれば幸いです。
以下は、URL を自然に含めた「ブログの最後に添えるパッチ文章」です。内容を壊さず、一般読者にも読みやすい形でまとめています。
🔍 補足:AWS Amplify と HttpOnly Cookie 対応について
今回の調査の中で、AWS Amplify(Authentication モジュール)が HttpOnly Cookie をサポートし始めているという公式アナウンスも確認しました。
特に、2025年3月に公開された以下の発表では、 Next.js などのサーバーサイドレンダリング(SSR)アプリ向けに、HttpOnly Cookie を用いたセキュアな認証管理がサポートされたと明記されています。
Cognito 自体が直接「HttpOnly Cookie に ID トークン/Access トークンを保存する」仕組みを提供しているわけではありませんが、 Amplify 側で Cookie ベースのセッション管理を実現するレイヤーを提供し始めたという点は非常に興味深い動きです。
この部分はまだ十分に検証できていないため、 Amplify がどのように Cookie を生成し、署名/暗号化し、検証するのか Nuxt / Nitro で同様の構成が再現できるのか といったポイントを引き続き調査し、別記事として整理する予定です。