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

使用Next-Auth实现OAuth时出现意外302错误

NextJS集成Google登录时持续出现302错误

问题描述

使用NextJS搭建带Google登录功能的网站,点击登录按钮后反复出现意外302错误。

相关截图

  • 首页截图:首页截图
  • 错误截图:错误截图

相关代码片段

"use client";

import Link from "next/link";
import Image from "next/image";
import { useEffect, useState } from "react";
import { signIn, signOut, useSession, getProviders } from "next-auth/react";

const Nav = () => {
  const { data: session } = useSession();

  const [providers, setProviders] = useState(null);
  const [toggleDropdown, setToggleDropdown] = useState(false);

  useEffect(() => {
    const initProviders = async () => {
      const response = await getProviders();
      setProviders(response);
    }
    initProviders();
  }, [])

  return (
    <nav className='flex-between w-full mb-16 pt-3'>
      <Link href='/' className='flex gap-2 flex-center'>
        <Image
          src='/assets/images/logo.svg'
          alt='logo'
          width={30}
          height={30}
          className='object-contain'
        />
        <p className='logo_text'>Rate My Landlord</p>
      </Link>

      {/* Desktop Navigation */}
      <div className='sm:flex hidden'>
        {session?.user ? (
          <div className='flex gap-3 md:gap-5'>
            <Link href='/create-prompt' className='black_btn'>
              Create Post
            </Link>

            <button type='button' onClick={signOut} className='outline_btn'>
              Sign Out
            </button>

            <Link href='/profile'>
              <Image
                src={session?.user.image}
                width={37}
                height={37}
                className='rounded-full'
                alt='profile'
              />
            </Link>
          </div>
        ) : (
          <>      
          {providers &&
              Object.values(providers).map((provider) => (
                <button
                  type='button'
                  key={provider.name}
                  onClick={() => {
                    signIn(provider.id);
                  }}
                  className='black_btn'
                >
                  Sign in
                </button>
              ))}
          </>
        )}
      </div>

      {/* Mobile Navigation */}
      <div className='sm:hidden flex relative'>
        {session?.user ? (
          <div className='flex'>
            <Image
              src={session?.user.image}
              width={37}
              height={37}
              className='rounded-full'
              alt='profile'
              onClick={() => setToggleDropdown(!toggleDropdown)}
            />

            {toggleDropdown && (
              <div className='dropdown'>
                <Link
                  href='/profile'
                  className='dropdown_link'
                  onClick={() => setToggleDropdown(false)}
                >
                  My Profile
                </Link>
                <Link
                  href='/create-prompt'
                  className='dropdown_link'
                  onClick={() => setToggleDropdown(false)}
                >
                  Create Prompt
                </Link>
                <button
                  type='button'
                  onClick={() => {
                    setToggleDropdown(false);
                    signOut();
                  }}
                  className='mt-5 w-full black_btn'
                >
                  Sign Out
                </button>
              </div>
            )}
          </div>
        ) : (
          <>
            {providers &&
              Object.values(providers).map((provider) => (
                <button
                  type='button'
                  key={provider.name}
                  onClick={() => {
                    signIn(provider.id);
                  }}
                  className='black_btn'
                >
                  Sign in
                </button>
              ))}
          </>
        )}
      </div>
    </nav>
  );
};

export default Nav;

NextAuth配置文件

import NextAuth from 'next-auth';
import GoogleProvider from 'next-auth/providers/google';

import { db } from '@utils/database';

const handler = NextAuth({
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_ID,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    })
  ],
  callbacks: {
    async session({ session }) {
      console.log("session");
      const sessionUser = await db.user.findUnique({ where: { email: session.user.email } });
      session.user.id = sessionUser._id.toString();
      
      return session;
    },
    async signIn({ account, profile, user, credentials }) {
      console.log("sign in");
      try {
        console.log(profile);
        const result = await db.user.findUnique({
          where: {
            email: profile.email
          }
        });
        console.log(result);
        if (!result) {
          console.log("the user is no found in the database");
          await db.user.create({
            data: {
              email: profile.email,
              name: profile.name,
            }
          });
        }

        return true
      } catch (error) {
        console.log("Error checking if user exists: ", error.message);
        return false
      }
    },
  }
})

export { handler as GET, handler as POST }

已在谷歌搜索数小时,未找到有效解决方案。


解决方案与调试步骤

1. 修复NextAuth回调错误处理

session回调未做错误捕获,若数据库查询失败会直接抛出错误引发302重定向。添加错误处理:

async session({ session }) {
  try {
    console.log("session");
    const sessionUser = await db.user.findUnique({ where: { email: session.user.email } });
    if (sessionUser) {
      session.user.id = sessionUser._id.toString();
    }
    return session;
  } catch (error) {
    console.error("Session callback error:", error);
    return session;
  }
},

2. 验证Google OAuth重定向URI

登录Google Cloud Console,检查OAuth客户端的授权重定向URI是否匹配:

  • 开发环境:http://localhost:3000/api/auth/callback/google
  • 生产环境:https://你的域名/api/auth/callback/google
    URI不匹配是302循环的最常见诱因。

3. 确认环境变量加载

检查.env.local中的GOOGLE_ID和GOOGLE_CLIENT_SECRET是否拼写正确、已生效。可在signIn回调中添加日志验证:

console.log("Loaded Google ID:", process.env.GOOGLE_ID);

4. 完善用户创建逻辑

Google返回的profile包含头像字段,但当前创建用户时未保存,若数据库模型要求该字段会导致创建失败,触发signIn回调返回false引发重定向。修改创建逻辑:

await db.user.create({
  data: {
    email: profile.email,
    name: profile.name,
    image: profile.picture // 匹配数据库模型字段
  }
});

5. 指定登录回调URL

修改signIn调用,显式指定回调URL避免重定向异常:

onClick={() => signIn(provider.id, { callbackUrl: '/' })}

调试技巧

  • 打开浏览器网络标签,追踪302重定向的完整路径,定位异常环节。
  • 查看服务端日志,检查signIn、session回调中的打印信息,确认哪一步出错。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 02:37:06