You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

无法从Clerk中间件获取用户邮箱,无法更新Supabase用户表

解决Clerk中间件中无法获取用户邮箱的问题

你的核心问题是在服务器端的Clerk中间件中误用了客户端API——getUser和getAuth都是@clerk/nextjs/client下的浏览器端方法,在服务器环境调用无法正确获取用户数据,自然拿不到邮箱。

正确解决方案:使用Clerk服务器端API

Clerk为服务器端场景提供了专门的API,你可以通过两种方式获取用户邮箱:

方式1:直接从auth()获取用户对象

Clerk中间件的auth()方法本身就会返回包含用户完整信息的对象,无需额外调用其他方法:

import { clerkMiddleware } from "@clerk/nextjs/server";
import { NextResponse } from "next/server";
import { supabase } from "./utils/supabase/supabase";

const isProtectedRoute = (req) => {
  const protectedPaths = ["/add-listing", "/(api|trpc)(.*)"];
  return protectedPaths.some((path) => req.url.includes(path));
};

export default clerkMiddleware(async (auth, req) => {
  console.log("Middleware is triggered");

  const { userId, user } = auth() || {};

  console.log("userId:", userId);
  console.log("User info:", user);

  if (userId && user) {
    console.log("User is authenticated:", userId);

    // 从user对象提取邮箱(优先取已验证的)
    const email = user.emailAddresses.find(e => e.verified)?.emailAddress || user.emailAddresses[0].emailAddress;
    const authId = userId;

    console.log("Email:", email);
    console.log("authId:", authId);
    const { error } = await supabase.from("Users").upsert({ email, authId });

    if (error) {
      console.error("Error inserting user into Supabase:", error);
    } else {
      console.log("User inserted or updated in Supabase successfully");
    }
  } else {
    console.log("User is not authenticated");
  }

  if (isProtectedRoute(req) && !userId) {
    return new Response("Unauthorized", { status: 401 });
  }

  return NextResponse.next();
});

export const config = {
  matcher: [
    "/add-listing",
    "/(api|trpc)(.*)",
    "/((?!_next|.*\\.).*)",
  ]
};

方式2:使用服务器端的getUser方法

如果你需要单独获取用户信息,可以从@clerk/nextjs/server导入服务器端的getUser:

import { clerkMiddleware, getUser } from "@clerk/nextjs/server";
import { NextResponse } from "next/server";
import { supabase } from "./utils/supabase/supabase";

const isProtectedRoute = (req) => {
  const protectedPaths = ["/add-listing", "/(api|trpc)(.*)"];
  return protectedPaths.some((path) => req.url.includes(path));
};

export default clerkMiddleware(async (auth, req) => {
  console.log("Middleware is triggered");

  const { userId } = auth() || {};

  console.log("userId:", userId);

  if (userId) {
    console.log("User is authenticated:", userId);

    // 调用服务器端API获取用户数据
    const user = await getUser(userId);
    // 提取邮箱(优先取已验证的)
    const email = user.emailAddresses.find(e => e.verified)?.emailAddress || user.emailAddresses[0].emailAddress;
    const authId = userId;

    console.log("Email:", email);
    console.log("authId:", authId);
    const { error } = await supabase.from("Users").upsert({ email, authId });

    if (error) {
      console.error("Error inserting user into Supabase:", error);
    } else {
      console.log("User inserted or updated in Supabase successfully");
    }
  } else {
    console.log("User is not authenticated");
  }

  if (isProtectedRoute(req) && !userId) {
    return new Response("Unauthorized", { status: 401 });
  }

  return NextResponse.next();
});

export const config = {
  matcher: [
    "/add-listing",
    "/(api|trpc)(.*)",
    "/((?!_next|.*\\.).*)",
  ]
};

关键注意点

  • 严格区分Clerk的客户端/服务器端API:@clerk/nextjs/client下的方法仅适用于浏览器组件,服务器端代码(中间件、API路由、Server Components)必须使用@clerk/nextjs/server下的API。
  • 用户邮箱存储在user.emailAddresses数组中(Clerk支持多邮箱),建议优先选取verified: true的邮箱,避免使用未验证的邮箱。

内容的提问来源于stack exchange,提问作者Anteneh Debebe Bekele

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 06:50:15