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

使用NextAuth与tRPC动态更新多租户NextJS应用的Session

多租户NextJS应用中动态更新NextAuth Session的解决方案

1. 配置NextAuth的JWT与Session回调

首先确保NextAuth使用JWT会话策略(这是动态更新session的前提),在[...nextauth].ts中配置回调,将orgId同步到JWT和Session:

import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { db } from "@/lib/db";

export const authOptions = {
  providers: [
    CredentialsProvider({
      async authorize(credentials) {
        // 登录逻辑:校验用户凭证,获取用户及其所属组织
        const user = await db.user.findUnique({
          where: { email: credentials?.email },
          include: { memberships: true },
        });
        if (!user) return null;
        
        // 设置初始orgId(默认取第一个有权限的组织)
        const initialOrgId = user.memberships[0]?.orgId;
        return { id: user.id, email: user.email, orgId: initialOrgId };
      },
    }),
  ],
  session: { strategy: "jwt" }, // 必须启用JWT模式
  callbacks: {
    async jwt({ token, user }) {
      // 登录时将orgId写入JWT
      if (user) token.orgId = user.orgId;
      return token;
    },
    async session({ session, token }) {
      // 将JWT中的orgId同步到前端Session
      if (session.user) session.user.orgId = token.orgId as string;
      return session;
    },
  },
};

export default NextAuth(authOptions);

2. 用tRPC实现切换组织的服务端接口

在tRPC路由中编写权限校验与Session更新逻辑,确保只有有权限的用户才能切换组织:

import { initTRPC, TRPCError } from "@trpc/server";
import { getServerSession } from "next-auth/next";
import { authOptions } from "@/app/api/auth/[...nextauth]/route";
import { db } from "@/lib/db";
import { z } from "zod";

const t = initTRPC.create();

export const appRouter = t.router({
  switchOrganization: t.procedure
    .input(z.object({ orgId: z.string() }))
    .mutation(async ({ input }) => {
      const session = await getServerSession(authOptions);
      if (!session?.user?.id) throw new TRPCError({ code: "UNAUTHORIZED" });

      // 校验用户是否拥有目标组织的访问权限
      const hasAccess = await db.membership.findFirst({
        where: { userId: session.user.id, orgId: input.orgId },
      });
      if (!hasAccess) throw new TRPCError({ code: "FORBIDDEN" });

      // 更新JWT中的orgId(NextAuth提供的update方法)
      const auth = await import("next-auth");
      await auth.update({
        token: { orgId: input.orgId },
      });

      return { success: true, orgId: input.orgId };
    }),
});

export type AppRouter = typeof appRouter;

3. 客户端触发切换并刷新Session

在前端组件中调用tRPC接口,完成后刷新Session以获取最新的orgId:

"use client";

import { useSession } from "next-auth/react";
import { api } from "@/trpc/react";

export default function OrgSwitcher() {
  const { data: session, refetch } = useSession();
  const switchOrgMutation = api.switchOrganization.useMutation({
    onSuccess: async () => {
      // 刷新Session,同步最新的orgId到前端
      await refetch();
    },
  });

  const handleSwitchOrg = (targetOrgId: string) => {
    switchOrgMutation.mutate({ orgId: targetOrgId });
  };

  return (
    <div className="flex gap-2">
      <span>当前组织: {session?.user.orgId}</span>
      <button onClick={() => handleSwitchOrg("org_xxxx")}>切换到目标组织</button>
    </div>
  );
}

核心注意事项

  • 必须使用JWT会话策略:数据库会话模式无法直接动态更新内容,JWT模式支持通过NextAuth的update方法修改字段。
  • 权限校验必须在服务端完成:禁止仅依赖客户端校验,防止恶意用户越权切换组织。
  • Session刷新:调用useSession的refetch方法会重新触发Session回调,拉取更新后的JWT数据,无需用户重新登录。

内容的提问来源于stack exchange,提问作者tdc

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 14:15:46