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

使用NextAuth.js凭证提供者并设redirect:false时出现URL构造错误

问题:NextAuth.js凭证登录(redirect: false)触发URL构造错误

开发基于Next.js 13 App目录的应用,使用NextAuth.js做身份验证、Prisma操作数据库、PlanetScale作为数据库服务。在使用凭证提供者(Credentials Provider)并设置signIn({redirect: false})时,输入正确凭证后触发如下错误:

TypeError: Failed to construct 'URL': Invalid URL
    at _callee6$ (webpack-internal:///(app-client)/./node_modules/next-auth/react/index.js:337:21)
    at tryCatch (webpack-internal:///(app-client)/./node_modules/@babel/runtime/helpers/regeneratorRuntime.js:44:17)
    at Generator.eval (webpack-internal:///(app-client)/./node_modules/@babel/runtime/helpers/regeneratorRuntime.js:125:22)
    at Generator.eval [as next] (webpack-internal:///(app-client)/./node_modules/@babel/runtime/helpers/regeneratorRuntime.js:69:21)
    at asyncGeneratorStep (webpack-internal:///(app-client)/./node_modules/@babel/runtime/helpers/asyncToGenerator.js:3:24)
    at _next (webpack-internal:///(app-client)/./node_modules/@babel/runtime/helpers/asyncToGenerator.js:22:9)

相关代码示例

auth.ts

const authOptions: NextAuthOptions = {
  adapter: PrismaAdapter(db),
  session: {
    strategy: 'jwt',
  },
  pages: {
    signIn: '/login',
  },
  providers: [
    GoogleProvider({
      clientId: envVariables.GOOGLE_CLIENT_ID,
      clientSecret: envVariables.GOOGLE_CLIENT_SECRET,
    }),
    Credentials({
      name: 'credentials',
      credentials: {
        email: {
          label: 'email',
          type: 'email',
          placeholder: 'wantnewstyle@mail.com',
        },
        password: {
          label: 'password',
          type: 'password',
        },
      },
      async authorize(credentials) {
        if (!credentials) {
          throw new Error('Грешни данни');
        }
        const dbUser = await db.user.findFirst({
          where: {
            email: credentials.email,
          },
        });

        if (!dbUser) {
          throw new Error('Грешен имейл');
        }

        // Check if passwords match
        const checkPassword = await bcrypt.compare(
          credentials.password,
          dbUser?.password ?? ''
        );

        if (!checkPassword) {
          throw new Error('Грешна парола');
        }
        return dbUser;
      },
    }),
  ],
  callbacks: {
    session({ token, session }) {
      if (token) {
        // eslint-disable-next-line no-param-reassign
        session.user.id = token.id;
        // eslint-disable-next-line no-param-reassign
        session.user.name = token.name;
        // eslint-disable-next-line no-param-reassign
        session.user.firstName = token.firstName;
        // eslint-disable-next-line no-param-reassign
        session.user.lastName = token.lastName;
        // eslint-disable-next-line no-param-reassign
        session.user.phone = token.phone;
        // eslint-disable-next-line no-param-reassign
        session.user.email = token.email;
        // eslint-disable-next-line no-param-reassign
        session.user.image = token.picture;
        // eslint-disable-next-line no-param-reassign
        session.user.isBarber = token.isBarber;
      }

      return session;
    },

    async jwt({ token, user }) {
      const dbUser = await db.user.findFirst({
        where: {
          email: token.email,
        },
        include: {
          barber: true,
        },
      });

      if (!dbUser) {
        // eslint-disable-next-line no-param-reassign
        token.id = user!.id;
        return token;
      }

      return {
        id: dbUser.id,
        firstName: dbUser.firstName!,
        lastName: dbUser.lastName!,
        email: dbUser.email,
        picture: dbUser.image,
        phone: dbUser.phone!,
        isBarber: dbUser.barber !== null,
      };
    },
    redirect() {
      return '/';
    },
  },
};

export default authOptions;

login-form.tsx

async function onSubmit(values: z.infer<typeof loginSchema>) {
    const result = await signIn('credentials', {
      email: values.email,
      password: values.password,
      redirect: false,
    });

    if (result?.error === 'Грешен имейл') {
      form.setError('email', {
        message: result.error,
      });
      return;
    }

    if (result?.error === 'Грешна парола') {
      form.setError('password', {
        message: result.error,
      });
      return;
    }

    await wait(3000);
    redirect('/');
  }
}

排查与修复建议

  • 确认NEXTAUTH_URL环境变量配置:NextAuth.js依赖该变量生成回调URL,即使redirect: false也会用到。开发环境需设置为http://localhost:3000,生产环境设置为实际域名,确保无拼写错误。
  • 移除硬编码的redirect回调:当前authOptions中的redirect() { return '/' }会覆盖NextAuth的默认URL处理逻辑,尤其是在redirect: false场景下可能引发冲突。尝试注释或删除该回调,让框架使用默认行为。
  • 验证authorize返回的用户对象完整性:确保authorize函数返回的用户对象包含id、email等核心字段,虽然当前代码逻辑看似正确,但不完整的对象可能间接导致URL构造异常。
  • 升级NextAuth.js版本:该错误可能是特定版本的已知bug,尝试升级到最新稳定版,查看是否已修复相关问题。

内容的提问来源于stack exchange,提问作者Dimitar D.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 08:50:24