Nuxt.js + SupabaseでGoogle OAuthログイン後にuseSupabaseUser()がundefinedになる問題を解決した話

Nuxt.js + SupabaseでGoogle OAuthログイン後にuseSupabaseUser()がundefinedになる問題を解決した話

はじめに

こんにちは!最近、Nuxt.js(v3)とSupabaseでGoogle OAuthログインを実装していたのですが、めちゃくちゃハマったので知見を共有します。

「Google OAuth認証は成功してるはずなのに、ログイン後にユーザー情報が取れない…」という経験ありませんか?まさに私がそうでした😭

この記事では、@nuxtjs/supabaseモジュールのuseSupabaseUser()がOAuth認証後にundefinedになってしまう問題と、その解決方法を詳しく解説します。

環境

{
  "nuxt": "^4.3.1",
  "@nuxtjs/supabase": "^2.0.3"
}

発生した問題

症状

Google OAuthでの認証自体は成功するのに、認証後のコールバックページ(/confirm)で10秒待ってもuseSupabaseUser()undefinedのまま。

<!-- pages/confirm.vue -->
<script setup lang="ts">
const user = useSupabaseUser();

onMounted(async () => {
  console.log("Initial user:", user.value?.id); // undefined

  // 10秒待機してみる
  let attempts = 0;
  while (!user.value?.id && attempts < 100) {
    await new Promise((resolve) => setTimeout(resolve, 100));
    attempts++;
    console.log("Waiting...", attempts, "user:", user.value?.id);
  }

  console.log("After waiting:", user.value?.id); // まだundefined!
});
</script>

コンソール出力:

Initial user: undefined
Waiting... 10 user: undefined
Waiting... 20 user: undefined
...
Waiting... 100 user: undefined
After waiting: undefined

何が起きているのか?

不思議なことに、supabase.auth.getSession()直接セッションを確認すると、ちゃんと存在するんです。

<script setup lang="ts">
const supabase = useSupabaseClient();

onMounted(async () => {
  const { data } = await supabase.auth.getSession();
  console.log("Session:", data.session?.user?.id);
  // → be510a98-58c7-482f-896f-a5cb88c387cc ✅ 取得できる!
});
</script>

つまり:

  • ✅ Supabaseのセッション自体は存在する
  • useSupabaseUser()がそれを反映しない

原因

@nuxtjs/supabaseモジュールのuseSupabaseUser()は、内部的にSupabaseのセッション情報を監視して自動的に同期するreactive変数を提供しています。

しかし、OAuth認証後のコールバック時に、この自動同期が期待通りに動作しないケースがあるようです。

特に以下のような流れだと問題が発生しやすいです:

  1. Google OAuth認証画面でログイン
  2. Supabaseの/auth/v1/callbackにリダイレクト
  3. アプリの/confirmページにリダイレクト ← このタイミングで同期されない
  4. useSupabaseUser()undefinedのまま

解決方法

結論から言うと、useSupabaseUser()に頼らず、supabase.auth.getSession()を直接使うのが確実です。

修正前(動かないコード)

<!-- pages/confirm.vue -->
<script setup lang="ts">
const user = useSupabaseUser();
const router = useRouter();

onMounted(async () => {
  // いくら待ってもuser.valueがundefined...
  while (!user.value?.id && attempts < maxAttempts) {
    await new Promise((resolve) => setTimeout(resolve, 100));
    attempts++;
  }

  if (user.value?.id) {
    router.push("/");
  }
});
</script>

修正後(動くコード)✨

<!-- pages/confirm.vue -->
<script setup lang="ts">
const supabase = useSupabaseClient();
const router = useRouter();

onMounted(async () => {
  // セッションを直接確認
  let attempts = 0;
  const maxAttempts = 100;
  let session = null;

  while (!session && attempts < maxAttempts) {
    const { data } = await supabase.auth.getSession();
    session = data.session;

    if (!session) {
      await new Promise((resolve) => setTimeout(resolve, 100));
      attempts++;
    }
  }

  if (session?.user?.id) {
    console.log("✅ ログイン成功:", session.user.email);
    await router.push("/");
  } else {
    console.log("❌ セッションなし");
    await router.push("/login");
  }
});
</script>

認証ミドルウェアも同様に修正

ダッシュボードページなど、認証が必要なページでも同じ問題が発生するので、ミドルウェアも修正します。

// middleware/auth.ts

// ❌ 修正前
export default defineNuxtRouteMiddleware(async (to) => {
  const user = useSupabaseUser();

  if (!user.value?.id) {
    return navigateTo("/login");
  }
});

// ✅ 修正後
export default defineNuxtRouteMiddleware(async (to) => {
  const supabase = useSupabaseClient();

  // セッションを直接確認(最大3秒待機)
  let session = null;
  let attempts = 0;
  const maxAttempts = 30;

  while (!session && attempts < maxAttempts) {
    const { data } = await supabase.auth.getSession();
    session = data.session;

    if (!session) {
      await new Promise((resolve) => setTimeout(resolve, 100));
      attempts++;
    }
  }

  if (!session?.user?.id) {
    return navigateTo("/login");
  }
});

その他の認証情報の取得方法

セッションを取得できれば、ユーザー情報も簡単に取り出せます:

const { data } = await supabase.auth.getSession();
const session = data.session;

if (session) {
  console.log("ユーザーID:", session.user.id);
  console.log("メールアドレス:", session.user.email);
  console.log("メタデータ:", session.user.user_metadata);
  console.log("アクセストークン:", session.access_token);
}

まとめ

  • 問題: @nuxtjs/supabaseuseSupabaseUser()がOAuth後にundefinedになる
  • 原因: 自動セッション同期が期待通りに動作しないケースがある
  • 解決: supabase.auth.getSession()を直接使ってセッションを確認する

フレームワークの便利機能に頼りすぎると、こういう罠にハマることがありますね😅

もし同じ問題で困っている方がいれば、ぜひこの方法を試してみてください!

参考リンク


追記(2026年2月):
この問題は@nuxtjs/supabaseのバージョンや実装環境によって発生しない場合もあります。しかし、OAuth認証後のセッション管理で問題が発生した際の対処法として知っておくと役立つと思います!

何か質問や他の解決方法があれば、コメントで教えてください 👋