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

Next-Auth生产环境登录回调异常问题排查求助

生产环境Next-Auth登录回调错误排查

我有一个基于Next.js和Next-Auth实现认证功能的项目,本地通过yarn dev运行、构建后用yarn build和yarn start启动均正常,但部署到生产环境后出现登录回调错误。

已查阅GitHub上相关问题,尝试配置NEXTAUTH_SECRET但未解决问题,以下是相关代码:

Next-Auth配置代码

export const authOptions: NextAuthOptions = {
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_ID,
      clientSecret: process.env.GOOGLE_SECRET,
    }),
    LineProvider({
      clientId: process.env.LINE_CLIENT_ID,
      clientSecret: process.env.LINE_CLIENT_SECRET,
    }),
    InstagramProvider({
      clientId: process.env.INSTAGRAM_CLIENT_ID,
      clientSecret: process.env.INSTAGRAM_SECRET_KEY,
    }),
    FacebookProvider({
      clientId: process.env.FACEBOOK_CLIENT_ID,
      clientSecret: process.env.FACEBOOK_CLIENT_SECRET,
      authorization: {
        params: { scope: undefined },
      },
    }),
    CredentialsProvider({
      id: 'ginCustomProvider',
      name: 'ginCustomProvider',
      credentials: {
        token: { type: 'text' },
      },
      async authorize(
        credentials
      ): Promise<Required<User & token & tokenGin> | null> {
        if (!credentials) return null;
        const res = await authSocialGin(credentials.token);
        if (!res) {
          return null;
        }
        return res;
      },
    }),
    CredentialsProvider({
      id: 'credentials',
      name: 'credentials',
      type: 'credentials',
      credentials: {
        username: { label: 'Username', type: 'text' },
        password: { label: 'Password', type: 'password' },
      },
      async authorize(credentials): Promise<Required<User & token> | null> {
        const res = await credentialLogin({
          username: credentials?.username,
          password: credentials?.password,
        });
        if (!res) {
          return null;
        }
        return res;
      },
    }),
  ],
  secret: process.env.NEXTAUTH_SECRET,
  session: {
    strategy: 'jwt',
  },
  theme: {
    colorScheme: 'light',
  },
  cookies: {
    sessionToken: {
      name: 'accessToken',
      options: { httpOnly: true, sameSite: 'lax', path: '/', secure: true },
    },
  },
  callbacks: {
    async jwt({ token, user, account }) {
      try {
        if (account && account?.provider === 'google' && account?.id_token) {
          const res = await authSocialGoogle(account.id_token);
          if (
            user &&
            isTokenExpired((<any>res)?.accessToken) &&
            (<any>res)?.refreshToken
          ) {
            const response = await requestToken((<any>res)?.refreshToken);
            return { ...token, ...response };
          }
          return {
            ...token,
            ...res,
            ...user,
          };
        }
        if (
          account &&
          account?.provider === 'instagram' &&
          account?.access_token
        ) {
          const res = await authSocialInstagram(account.access_token);
          if (
            user &&
            isTokenExpired((<any>res)?.accessToken) &&
            (<any>res)?.refreshToken
          ) {
            const response = await requestToken((<any>res)?.refreshToken);
            return { ...token, ...response };
          }
          return {
            ...token,
            ...res,
            ...user,
          };
        }
        if (
          account &&
          account?.provider === 'facebook' &&
          account?.access_token
        ) {
          const res = await authSocialFaceBook(account.access_token);
          if (
            user &&
            isTokenExpired((<any>res)?.accessToken) &&
            (<any>res)?.refreshToken
          ) {
            const response = await requestToken((<any>res)?.refreshToken);
            return { ...token, ...response };
          }
          return {
            ...token,
            ...res,
            ...user,
          };
        }
        if (account && account?.provider === 'line' && account?.id_token) {
          const res = await authSocialLine(account.id_token);
          return {
            ...token,
            ...user,
            ...res,
          };
        }
        if (account && account?.provider === 'credentials') {
          return {
            ...token,
            ...user,
          };
        }
        if (account && account?.provider === 'ginCustomProvider') {
          return {
            ...token,
            ...user,
          };
        }
        if (
          token &&
          isTokenExpired(<any>token?.accessToken) &&
          token?.refreshToken
        ) {
          const responseRefreshToken = await requestToken(
            (<any>token)?.refreshToken
          );
          return { ...token, ...responseRefreshToken };
        }
        return token;
      } catch (error) {
        throw error;
      }
    },
    async session({ session, token }): Promise<Session> {
      const tokenId = token.accessToken;
      try {
        if (tokenId) {
          session.user = tokenId;
        } else {
          session.user = { name: '', email: '' };
        }
        return session;
      } catch (error) {
        throw error;
      }
    },
    async redirect({ url, baseUrl }) {
      // Allows relative callback URLs
      if (url.startsWith('/ginCustomProvider')) {
        const fullUrl = `${baseUrl}/${url}`;
        return fullUrl.split('?')[0];
      }
      // Allows callback URLs on the same origin
      else if (new URL(url).origin === baseUrl) return url;
      return baseUrl;
    },
  },
};

export default NextAuth(authOptions);

package.json内容

{
  "name": "minakomi-frontend-webpage",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint"
  },
  "dependencies": {
    "@emotion/react": "^11.10.5",
    "@emotion/styled": "^11.10.5",
    "@hookform/resolvers": "^2.9.10",
    "@mui/icons-material": "^5.10.16",
    "@mui/material": "^5.10.17",
    "@mui/x-date-pickers": "^5.0.7",
    "@reduxjs/toolkit": "^1.9.0",
    "@sls-next/serverless-component": "3.7.0",
    "ahooks": "^3.7.2",
    "axios": "^1.1.3",
    "dayjs": "^1.11.6",
    "i18next": "^22.0.6",
    "lodash": "^4.17.21",
    "moment": "^2.29.4",
    "next": "12.2.5",
    "next-auth": "4.15.1",
    "next-i18next": "^13.0.0",
    "next-share": "^0.19.0",
    "notistack": "^2.0.8",
    "qs": "^6.11.0",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-hook-form": "^7.39.3",
    "react-i18next": "^12.0.0",
    "react-infinite-scroll-component": "^6.1.0",
    "react-loader-spinner": "^5.3.4",
    "react-phone-number-input": "^3.2.12",
    "react-query": "^3.39.2",
    "react-redux": "^8.0.5",
    "react-toastify": "^9.1.1",
    "styled-components": "^5.3.5",
    "yup": "^0.32.11"
  },
  "devDependencies": {
    "@babel/parser": "^7.19.1",
    "@types/node": "^18.7.18",
    "@types/qs": "^6.9.7",
    "@types/react": "18.0.20",
    "@types/react-dom": "18.0.6",
    "@types/styled-components": "^5.1.26",
    "@typescript-eslint/eslint-plugin": "^5.42.0",
    "@typescript-eslint/parser": "^5.42.0",
    "eslint": "^8.26.0",
    "eslint-config-next": "12.3.0",
    "eslint-config-prettier": "^8.5.0",
    "eslint-import-resolver-typescript": "^3.5.2",
    "eslint-plugin-prettier": "^4.2.1",
    "prettier": "^2.7.1",
    "typescript": "4.8.3"
  }
}

可能的原因分析

  • 回调URL配置不匹配
    第三方登录提供商(Google/Line/Facebook/Instagram)后台配置的回调URL必须和生产环境实际域名完全一致,包括http/https、端口(非默认时)。比如生产环境用HTTPS,回调URL必须是https://your-domain.com/api/auth/callback/[provider],不能用HTTP或本地域名。

  • Cookie配置问题
    你设置了cookies.sessionToken.options.secure: true,该配置要求网站必须运行在HTTPS环境下。如果生产环境未启用HTTPS,浏览器会拒绝保存此Cookie,导致登录状态无法维持,回调后认证失败。另外sameSite: 'lax'在跨域回调场景下可能存在限制,若为跨域登录回调,可尝试调整为'none',但必须配合secure: true。

  • 环境变量未正确加载
    确认生产环境下所有必要环境变量(NEXTAUTH_SECRET、各提供商的clientId/clientSecret等)都已正确配置且无拼写错误。部分部署平台需手动设置环境变量,不会自动读取.env文件。NEXTAUTH_SECRET必须是足够复杂的随机字符串,生产环境不能留空或使用开发环境测试值。

  • Redirect回调逻辑问题
    你的redirect回调中处理/ginCustomProvider的逻辑会生成带重复斜杠的URL(如https://domain.com//ginCustomProvider),可能导致跳转失败。应改为new URL(url, baseUrl).toString()来正确拼接URL,避免路径错误。

  • Serverless部署特殊问题
    你使用@sls-next/serverless-component进行Serverless部署,这种模式下Next.js API路由运行在无服务器环境中,可能存在冷启动、请求超时或会话存储问题。检查Serverless函数日志,查看回调过程中是否有超时或错误信息。同时确保NEXTAUTH_URL环境变量已正确设置为生产环境完整域名,Next-Auth需要此值生成正确回调URL。

  • JWT回调错误处理问题
    你的jwt回调中直接throw error,若第三方登录API请求在生产环境失败(如网络问题、API密钥无效),会中断整个回调流程。建议添加更友好的错误处理,比如返回原始token或记录错误日志,而非直接抛出错误。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 11:50:23