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

NextAuth.js GitHub登录返回AccessDenied,profile未定义问题求助

NextAuth.js GitHub登录报错:Unauthorized - Session not found 访问被拒绝

问题现象

  • 终端抛出错误:Error: Unauthorized - Session not found
  • 浏览器跳转至http://localhost:3000/api/auth/error?error=AccessDenied,提示访问被拒绝
  • Navbar组件中console.log("Session:", session)始终返回null

已确认配置

  • .env文件中正确配置了GITHUB_ID和GITHUB_SECRET
  • GitHub OAuth应用的授权回调URL设置为http://localhost:3000/api/auth/callback/github
  • 使用环境:Next.js App Router(最新版) + NextAuth v4 + Sanity CMS

相关代码片段

auth.ts 认证逻辑

import NextAuth from "next-auth";
import GitHub from "next-auth/providers/github";
import { AUTHOR_BY_GITHUB_ID_QUERY } from "@/sanity/lib/queries";
import { client } from "@/sanity/lib/client";
import { writeClient } from "@/sanity/lib/write-client";

export const { handlers, auth, signIn, signOut } = NextAuth({
  providers: [GitHub],
  callbacks: {
    async signIn({ user: { name, email, image }, profile }) {
      console.log("SIGNIN PROFILE:", profile);

      if (!profile) {
        console.error("Profile missing!");
        return false;
      }

      const { id, login, bio } = profile;

      const existingUser = await client
        .withConfig({ useCdn: false })
        .fetch(AUTHOR_BY_GITHUB_ID_QUERY, {
          id,
        });
      console.log("Existing user:", existingUser);

      if (!existingUser) {
        await writeClient.create({
          _id: `author.${id}`,
          _type: "author",
          id,
          name,
          username: login,
          email,
          image,
          bio: bio || "",
        });
      }

      return true;
    },
    async jwt({ token, account, profile }) {
      if (account && profile) {
        const user = await client
          .withConfig({ useCdn: false })
          .fetch(AUTHOR_BY_GITHUB_ID_QUERY, {
            id: profile?.id,
          });

        token.id = user?._id;
      }

      return token;
    },
    async session({ session, token }) {
      Object.assign(session, { id: token.id });
      return session;
    },
  },
});

next-auth.d.ts 类型声明

import { DefaultSession } from "next-auth";

declare module "next-auth" {
  interface Session {
    user: {
      id: string;
    } & DefaultSession["user"];
  }
}

declare module "next-auth/jwt" {
  interface JWT {
    id: string;
  }
}
// app/components/Navbar.tsx
import { auth } from "@/auth";
import Image from "next/image";
import Link from "next/link";
import { LoginButton, LogoutButton } from "./AuthButtons";

const Navbar = async () => {
  const session = await auth();
  console.log("Session:", session);

  return (
    <header className="px-5 py-3 bg-white shadow-sm font-work-sans">
      <nav className="flex justify-between items-center">
        <Link href="/">
          <Image src="/logo.png" alt="logo" width={144} height={30} />
        </Link>

        <div className="flex items-center gap-5 text-black">
          {session && session?.user ? (
            <>
              <Link href="/startup/create">
                <span>Create</span>
              </Link>
              <LogoutButton />
              <Link href={`/user/${session?.user?.id}`}>
                {session?.user?.name}
              </Link>
            </>
          ) : (
            <LoginButton />
          )}
        </div>
      </nav>
    </header>
  );
};

export default Navbar;

排查与解决方向

1. 捕获signIn回调中的异步异常

当前signIn回调中的Sanity查询/创建用户操作未做错误捕获,一旦出现网络错误、权限错误或查询失败,会直接导致回调返回false,触发AccessDenied。添加try/catch块排查:

async signIn({ user: { name, email, image }, profile }) {
  console.log("SIGNIN PROFILE:", profile);

  if (!profile) {
    console.error("Profile missing!");
    return false;
  }

  const { id, login, bio } = profile;

  try {
    const existingUser = await client
      .withConfig({ useCdn: false })
      .fetch(AUTHOR_BY_GITHUB_ID_QUERY, { id });
    console.log("Existing user:", existingUser);

    if (!existingUser) {
      await writeClient.create({
        _id: `author.${id}`,
        _type: "author",
        id,
        name,
        username: login,
        email,
        image,
        bio: bio || "",
      });
    }
    return true;
  } catch (err) {
    console.error("SignIn callback error:", err);
    return false;
  }
},

2. 显式指定Session策略

NextAuth v4默认使用JWT策略,但显式声明可避免潜在的配置问题:

export const { handlers, auth, signIn, signOut } = NextAuth({
  providers: [GitHub],
  session: { strategy: "jwt" }, // 显式配置JWT策略
  callbacks: { /* ... */ },
});

3. 修正Session回调的赋值逻辑

根据你扩展的Session类型,应将id赋值给session.user而非直接给session对象,否则会导致类型不兼容和数据丢失:

async session({ session, token }) {
  if (session.user) {
    session.user.id = token.id as string;
  }
  return session;
},

4. 验证Sanity查询语句

确认AUTHOR_BY_GITHUB_ID_QUERY的GROQ查询是否正确,需确保能通过GitHub id匹配到Sanity中的author文档:

*[_type == "author" && id == $id][0]

如果查询返回undefined,会导致token.id为空,进而影响session生成。

5. 检查环境变量加载

在auth.ts开头添加日志,确认环境变量是否正确加载:

console.log("GITHUB_ID:", process.env.GITHUB_ID);
console.log("GITHUB_SECRET:", process.env.GITHUB_SECRET);

注意Next.js默认优先加载.env.local文件,确保变量存放在正确的文件中。

6. 确认NextAuth路由入口

确保在app/api/auth/[...nextauth]/route.ts中正确导出handlers:

import { handlers } from "@/auth";
export const { GET, POST } = handlers;

该文件是App Router模式下NextAuth处理认证请求的必要入口,缺失会导致认证流程失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 09:39:52