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

Next.JS认证回调页TRPCClientError:非有效JSON致无法跳转

解决Next.js认证回调页面的TRPCClientError:Unexpected token '<'错误

问题描述

在Next.js的src/app/auth-callback/page.tsx认证回调页面调用tRPC的authCallback接口时,触发以下错误:

TRPCClientError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON

页面卡在加载状态,无法按预期跳转到仪表盘或登录页。

相关客户端页面代码:

'use client'

import { useRouter, useSearchParams } from 'next/navigation'
import { trpc } from '../_trpc/client'

const Page = () => {
  const router = useRouter()

  const searchParams = useSearchParams()
  const origin = searchParams.get('origin')

  trpc.authCallback.useQuery(undefined, {
    onSuccess: ({ success }) => {
      if (success) {
        router.push(origin ? `/${origin}` : '/dashboard')
      }
    },
    onError: err => {
      if (err.data?.code === 'UNAUTHORIZED') {
        router.push('/sign-in')
      }
    },
  })

  return (
    <div>
      <div>
        <p>You will be redirected automatically.</p>
      </div>
    </div>
  )
}

export default Page

对应的tRPC authCallback Procedure代码:

authCallback: publicProcedure.query(async () => {
  const { getUser } = getKindeServerSession()
  const user = getUser()
  console.log('auth user', user)
  if (!user.id || !user.email) throw new TRPCError({ code: 'UNAUTHORIZED' })

  const dbUser = await db.user.findFirst({
    where: {
      id: user.id,
    },
  })

  if (!dbUser) {
    await db.user.create({
      data: {
        id: user.id,
        email: user.email,
      },
    })
  }

  return { success: true }
}),

原因分析

这个错误的核心是tRPC客户端收到的不是预期的JSON响应,而是HTML内容。具体到这个场景:

  • getKindeServerSession()是服务器端专属方法,无法在客户端组件中正常执行,会触发服务器端错误,导致返回HTML错误页面而非JSON响应。
  • 客户端调用tRPC接口时,服务器返回的HTML被当作JSON解析,自然触发语法错误。

解决方案

将认证回调逻辑迁移到服务器组件中,直接使用Next.js的服务器端能力处理会话验证和跳转,无需通过tRPC:

修改src/app/auth-callback/page.tsx为服务器组件:

import { redirect } from 'next/navigation'
import { db } from '@/db' // 替换为你的数据库导入路径
import { getKindeServerSession } from '@kinde-oss/kinde-auth-nextjs/server'

const Page = async ({ searchParams }: { searchParams: { origin?: string } }) => {
  const { getUser } = getKindeServerSession()
  const user = getUser()

  // 验证用户会话,失败则跳转到登录页
  if (!user.id || !user.email) {
    redirect('/sign-in')
  }

  // 检查并创建数据库用户
  const dbUser = await db.user.findFirst({
    where: { id: user.id },
  })
  if (!dbUser) {
    await db.user.create({
      data: { id: user.id, email: user.email },
    })
  }

  // 跳转到目标页面
  redirect(searchParams.origin ? `/${searchParams.origin}` : '/dashboard')
}

export default Page

为什么这样解决?

  1. 服务器组件可以直接调用Kinde的服务器端会话方法,确保正确获取用户信息,不会触发环境不兼容错误。
  2. 使用Next.js内置的redirect方法直接完成跳转,避免了客户端到服务器的额外tRPC请求,流程更高效。
  3. 彻底规避了HTML响应被当作JSON解析的问题,从根源解决错误。

备选方案(保留tRPC)

如果一定要通过tRPC处理逻辑,需要确保authCallback Procedure在服务器端正确执行:

  1. 确保tRPC路由配置正确,/api/trpc接口未被其他页面路由覆盖。
  2. 检查publicProcedure是否允许执行服务器端逻辑,若需要会话验证,应使用带会话中间件的Procedure(比如protectedProcedure)。
  3. 避免在tRPC Procedure中调用只能在特定环境运行的方法,确保所有依赖的服务(如Kinde)能在tRPC的执行环境中正常工作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 21:23:15