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

NextAuth多租户SaaS应用:如何在signIn回调中获取登录域名?

在NextAuth的signIn回调中获取登录域名/主机名的方案

在NextAuth(现Auth.js)的signIn回调中,你可以通过context参数里的req对象直接获取请求头中的主机信息,具体实现如下:

1. 基础场景:直接从请求头获取主机名

在[...nextauth].js的配置中,signIn回调的context参数包含了请求对象req,你可以通过req.headers.host拿到当前登录请求的域名/子域名:

import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";

// 自定义函数:从主机名解析租户标识
const extractTenantFromHost = (host) => {
  // 示例逻辑:拆分子域名(假设主域名为your-saas.com)
  const parts = host.split(".");
  if (parts.length >= 3 && parts[parts.length - 2] === "your-saas") {
    return parts[0]; // 返回子域名作为租户ID
  }
  // 处理自定义域名:查询数据库匹配已绑定的租户
  return findTenantByCustomDomain(host); // 需自行实现数据库查询逻辑
};

// 自定义函数:验证用户是否属于当前租户
const checkUserTenant = async (userId, tenantDomain) => {
  // 示例:查询用户-租户关联表验证权限
  const userTenant = await db.userTenants.findFirst({
    where: { userId, tenantDomain }
  });
  return !!userTenant;
};

export default NextAuth({
  providers: [
    CredentialsProvider({
      name: "Credentials",
      credentials: {
        email: { label: "Email", type: "email" },
        password: { label: "Password", type: "password" }
      },
      async authorize(credentials, req) {
        // 实现用户凭证验证逻辑
        const user = await db.users.findUnique({
          where: { email: credentials.email }
        });
        if (!user || !await verifyPassword(credentials.password, user.passwordHash)) {
          return null;
        }
        return user;
      }
    })
  ],
  callbacks: {
    async signIn({ user, context }) {
      // 获取当前请求的主机名
      const host = context.req.headers.host;
      
      // 解析租户信息
      const tenantDomain = extractTenantFromHost(host);
      if (!tenantDomain) {
        return false; // 非法域名,阻止登录
      }

      // 验证用户是否属于该租户
      const isAuthorized = await checkUserTenant(user.id, tenantDomain);
      return isAuthorized;
    }
  }
});

2. 反向代理场景:处理x-forwarded-host头

如果你的SaaS部署在使用反向代理的环境(如Vercel、Nginx、AWS Amplify),req.headers.host可能返回代理服务器的域名而非用户实际访问的域名。此时需要使用x-forwarded-host请求头,同时配置Next.js信任代理:

步骤1:配置Next.js信任代理

在next.config.js中添加代理信任配置:

module.exports = {
  // 适用于Pages Router
  serverRuntimeConfig: {
    trustProxy: true
  },
  // 适用于App Router(Next.js 13+)
  experimental: {
    trustProxy: true
  }
};

步骤2:修改主机名获取逻辑

在signIn回调中优先读取x-forwarded-host:

const getRequestHost = (req) => {
  // 处理可能的数组格式(部分代理会传递多个值)
  const forwardedHost = req.headers["x-forwarded-host"];
  if (forwardedHost) {
    return Array.isArray(forwardedHost) ? forwardedHost[0] : forwardedHost;
  }
  // 回退到默认host头
  return req.headers.host;
};

// 在signIn回调中使用
async signIn({ user, context }) {
  const host = getRequestHost(context.req);
  // ...后续租户验证逻辑
}

3. 安全注意事项

  • 务必验证获取到的主机名是否属于你的SaaS允许的范围(已注册的子域名、已验证的自定义CNAME域名),避免恶意请求伪造x-forwarded-host头绕过验证。
  • 对于自定义域名,要提前在数据库中存储已绑定的租户-域名映射关系,验证时直接匹配该映射表。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 10:35:15