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

NextAuth中首次登录用户重定向并传递Google用户信息的问题

问题

我用NextAuth实现了Google OAuth登录,在signIn异步回调里通过getUser查询数据库判断是否首次登录,首次登录就重定向到/sign-up页面,但在这个页面里获取到的session是null。我不想用NextAuth的适配器,因为它会创建多张表,我想尽量减少数据库存储的数据量。

技术栈:Next.js App Router(14.2.16)、NextAuth v5.0.0(beta)

auth.ts 代码:

export const { handlers, auth } = NextAuth({
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID as string,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
    }),
  ],
  callbacks: {
    async signIn({ user, account, profile }) {
      if (account?.provider === "google") {
        const isNewUSer = await getUser({ uuid: profile.sub });

        if (!isNewUSer) {
          return true;
        }
      }
      return "/sign-up";
    },
    async jwt({ token, account, profile }) {
      if (account && profile) {
        token.sub = profile.sub;
        token.name = profile.name;
        token.email = profile.email;
        token.isNewUser = false;
      }

      return token;
    },
    async session({ session, token }) {
      if (session.user) {
        session.user.id = token.sub;
        session.user.isNewUser = token.isNewUser as boolean;
      }
      return session;
    },
  },
  pages: {
    signIn: "/login",
    newUser: "/sign-up",
  },
});

/sign-up/page.tsx 代码:

import { auth } from "@/auth";

export default async function SignUp() {
  const session = await auth();
  console.log("sign-up session = ", session); // 已Google登录,但session为null
  return (
    <div>
      <form>
        {/* 该表单提交操作会将昵称、uuid、邮箱等用户信息存储到数据库 */}
        <input type="text" placeholder="Enter your Nickname" name="nickname" />
        <button>Submit</button>
      </form>
    </div>
  );
}

需要解决的问题:如何在重定向新用户到/sign-up页面时,传递从Google获取到的用户信息?


解决方案

问题核心是返回/sign-up时NextAuth还未完成session创建,导致新用户访问该页面拿不到session。以下是无需适配器的解决思路:

1. 修改signIn回调,临时存储Google用户信息到Cookie

先安装cookie库处理Cookie:

npm install cookie

然后修改auth.ts的signIn回调,将Google返回的用户信息加密后存入临时Cookie,再重定向:

import { serialize } from 'cookie';

// ... 其他代码

async signIn({ user, account, profile }) {
  if (account?.provider === "google") {
    const existingUser = await getUser({ uuid: profile.sub });
    if (existingUser) {
      return true;
    }
    // 新用户:加密存储Google用户信息到临时Cookie
    const tempUserInfo = serialize(
      'temp_google_user',
      JSON.stringify({
        sub: profile.sub,
        name: profile.name,
        email: profile.email,
      }),
      {
        httpOnly: true,
        secure: process.env.NODE_ENV === 'production',
        sameSite: 'lax',
        maxAge: 60 * 5, // 5分钟过期,降低安全风险
        path: '/sign-up', // 仅允许/sign-up页面访问
      }
    );
    // 返回带Cookie的重定向响应
    return {
      redirect: {
        destination: '/sign-up',
        permanent: false,
      },
      headers: {
        'Set-Cookie': tempUserInfo,
      },
    };
  }
  return true;
}

2. 在/sign-up页面读取临时Cookie获取用户信息

修改/sign-up/page.tsx,从Cookie中解析临时存储的Google用户信息:

import { cookies } from 'next/headers';
import { redirect } from 'next/navigation';

export default async function SignUp() {
  const cookieStore = cookies();
  const tempUserCookie = cookieStore.get('temp_google_user');
  
  if (!tempUserCookie) {
    // 非登录跳转来的用户直接重定向到登录页
    redirect('/login');
  }

  const googleUser = JSON.parse(tempUserCookie.value);
  console.log('Google用户信息:', googleUser); // 可拿到sub、name、email等字段

  // 表单提交逻辑:将Google信息和昵称存入数据库
  const handleSubmit = async (formData: FormData) => {
    const nickname = formData.get('nickname') as string;
    
    await fetch('/api/sign-up', {
      method: 'POST',
      body: JSON.stringify({
        nickname,
        uuid: googleUser.sub,
        email: googleUser.email,
        name: googleUser.name,
      }),
      headers: {
        'Content-Type': 'application/json',
      },
    });

    // 提交成功后删除临时Cookie,并重定向触发Session创建
    cookieStore.delete('temp_google_user');
    redirect('/');
  };

  return (
    <div>
      <form action={handleSubmit}>
        <input type="text" placeholder="Enter your Nickname" name="nickname" required />
        <button type="submit">Submit</button>
      </form>
    </div>
  );
}

3. 完善JWT回调逻辑(可选)

调整JWT回调,动态判断用户是否为新用户,确保后续Session正常生成:

async jwt({ token, account, profile }) {
  if (account && profile) {
    const existingUser = await getUser({ uuid: profile.sub });
    token.sub = profile.sub;
    token.name = profile.name;
    token.email = profile.email;
    token.isNewUser = !existingUser; // 不存在则标记为新用户
  }
  return token;
}

内容的提问来源于stack exchange,提问作者이준희

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 16:12:04