切换语言
切换主题

Next.js OAuth登录实战:手把手接入Google、GitHub、微信第三方登录

上周接了个社区项目,产品经理丢过来一句:“加个Google登录吧,应该很快。“我想着不就是第三方登录嘛,之前也看过几篇教程,半小时搞定。结果配了一下午,各种redirect_uri错误、token获取失败,控制台的红色错误信息一条接一条。最崩溃的是,明明按着官方文档一步步来的,就是跑不通。

后来我发现,问题不在代码,而是我对OAuth流程理解得太浅了。什么authorization code、access token、callback这些概念,每个单独看都懂,但组合起来就蒙圈。如果你也被第三方登录折磨过,或者正准备给Next.js项目加登录功能,这篇文章能帮到你。

这次我会用大白话把OAuth流程讲清楚——不是照搬RFC文档那种抽象描述,而是用”代取快递”这种生活场景来类比。你会知道为什么需要code和token两个东西,callback到底在干嘛,以及那些容易出错的配置细节。然后手把手带你配置三种登录:Google(国际标准)、GitHub(开发者友好)、微信(国内必备但最折腾)。

说实话,微信登录是最坑的部分,文档不友好、需要企业资质、本地调试麻烦。但既然做国内项目绕不开,我就把踩过的坑都写出来,包括怎么用内网穿透调试、怎么配置自定义Provider。读完这篇,你应该能用一套代码架构搞定国内外登录需求。

OAuth流程到底是怎么回事

用代取快递理解OAuth

我第一次接触OAuth的时候,看到authorization code、access token这些概念就头大。后来想明白了,就是代取快递的逻辑。

想象一下:你在快递站有个包裹(用户信息),但你现在在公司,没法亲自去拿。这时候你朋友(你的Next.js应用)说帮你代取。快递站不能随便把东西给别人,得确认是你授权的,所以流程是这样的:

1. 你给朋友一个取件码 - 这就是authorization code。你点击”用Google登录”按钮后,会跳转到Google的授权页面,点同意后,Google会生成一个临时的code,通过URL参数传给你的应用。

2. 朋友拿着取件码去快递站 - 这就是你的应用后端用code去换access token。但快递站还要核实一下,这个朋友是不是你真正信任的人,所以需要你提前在快递站登记过朋友的身份证号(client_secret)。

3. 核实身份后给快递 - 快递站确认取件码+身份证号都对,就把包裹(用户信息)给朋友,朋友再交给你。这就是用access_token获取用户信息的过程。

关键点在这:取件码(code)是一次性的,而且任何人看到都没用,因为没有身份证号(secret)配合,快递站不会给东西。这就是为什么code可以在浏览器URL里明文传输,但secret必须藏在后端。

四个关键步骤拆解

具体到技术流程,就是这四步:

步骤1:跳转到OAuth服务器授权页面

用户点击”用Google登录”,你的前端会构造一个URL,把用户重定向到Google:

https://accounts.google.com/o/oauth2/v2/auth?
  client_id=你的应用ID
  &redirect_uri=http://localhost:3000/api/auth/callback/google
  &response_type=code
  &scope=email profile

这里面几个参数:

  • client_id:你的应用在Google那边的身份证
  • redirect_uri:Google授权后把用户送回哪
  • scope:你要访问哪些用户信息

步骤2:用户同意授权,获得code

用户在Google页面点”允许”后,Google会重定向回你的应用,URL变成:

http://localhost:3000/api/auth/callback/google?code=4/0AfJohXl...&state=random123

这个code就是取件码,有效期很短,一般只有5-10分钟,而且只能用一次。

步骤3:后端用code换access_token

你的应用后端(注意是后端,不是前端)拿着code,再加上client_secret,发请求给Google:

const response = await fetch('https://oauth2.googleapis.com/token', {
  method: 'POST',
  body: JSON.stringify({
    code: '刚才拿到的code',
    client_id: 'your_client_id',
    client_secret: 'your_secret', // 这个绝不能暴露给前端
    redirect_uri: 'http://localhost:3000/api/auth/callback/google',
    grant_type: 'authorization_code'
  })
})

const { access_token } = await response.json()

拿到access_token后,这才是真正能获取用户信息的钥匙。

步骤4:用access_token获取用户信息

有了token,就能调用Google的API获取用户数据了:

const userInfo = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {
  headers: {
    Authorization: `Bearer ${access_token}`
  }
})

const user = await userInfo.json()
// { email: "user@gmail.com", name: "张三", picture: "头像URL" }

拿到用户信息后,你就可以在自己的数据库里创建或更新用户记录,然后生成session,完成登录。

关键概念速查表

我把常见术语整理了下,方便对照:

术语通俗解释在哪里能看到
Client ID你应用的身份证号,可以公开.env.local文件,浏览器URL参数
Client Secret应用的密码,绝对保密只能在后端代码和环境变量里
Authorization Code一次性取件码回调URL的code参数
Access Token真正能获取信息的钥匙后端代码里,不传给前端
Redirect URI授权后回跳的地址OAuth应用配置、授权URL参数
Scope要访问的权限范围授权URL参数,如”email profile”
State防CSRF攻击的随机字符串授权URL参数和回调URL参数

State参数的作用:你发起授权时生成一个随机字符串(比如”abc123”),存在session里,同时传给OAuth服务器。回调时检查返回的state是不是”abc123”。如果不是,说明可能被攻击了,直接拒绝。NextAuth.js会自动帮你处理这个。

为什么不能直接返回token?

你可能会想,既然最终需要的是access_token,为啥不直接在URL里返回token,还要多一步code换token?

安全考虑。

浏览器地址栏的URL是明文的,会被浏览器历史记录、服务器日志、浏览器插件看到。如果直接返回token,等于把用户信息的钥匙明文传播了,风险太大。

而code即使被截获也没用,因为:

  1. Code只能用一次,用过就作废
  2. 换token必须同时提供client_secret,这个只有你的后端知道
  3. OAuth服务器还会验证redirect_uri是否匹配

这样就算攻击者截获了code,没有secret也换不到token,用户信息还是安全的。

这个设计叫”Authorization Code Flow”,是OAuth 2.0最安全的一种授权方式,特别适合有后端服务器的Web应用。还有种”Implicit Flow”直接返回token,但已经不推荐用了,太不安全。

NextAuth.js快速上手

为什么选NextAuth.js

Next.js OAuth登录有几种实现方式,可以完全手写,也可以用库。我试过自己写,踩了无数坑后果断投降,改用NextAuth.js。

为啥推荐它:

原因1:官方背书,生态成熟

NextAuth.js是Next.js官方文档推荐的认证方案,GitHub上7万+星,维护活跃。2024年11月刚发布的v5版本完美支持App Router和Server Components,不用担心兼容性问题。

原因2:内置30+OAuth提供者

Google、GitHub、Facebook、Twitter这些常见的开箱即用,配置几行代码就能跑。对于像微信这种没内置的,也提供了自定义Provider的机制,不用从零写OAuth流程。

原因3:自动处理复杂逻辑

Session管理、JWT签名、数据库存储、CSRF防护,这些全自动。你只需要关注业务逻辑,比如”用户第一次登录时要不要发欢迎邮件”。

核心配置文件结构

NextAuth.js的核心是一个API route,如果你用的是App Router(Next.js 13+),文件路径是:

app/api/auth/[...nextauth]/route.ts

这个[...nextauth]是Next.js的catch-all route,意思是所有/api/auth/*的请求都会被这个文件处理,包括:

  • /api/auth/signin - 登录页
  • /api/auth/callback/google - Google回调
  • /api/auth/signout - 登出
  • /api/auth/session - 获取当前session

基础配置长这样:

// app/api/auth/[...nextauth]/route.ts
import NextAuth from "next-auth"
import GoogleProvider from "next-auth/providers/google"
import GitHubProvider from "next-auth/providers/github"

export const authOptions = {
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    }),
    GitHubProvider({
      clientId: process.env.GITHUB_ID!,
      clientSecret: process.env.GITHUB_SECRET!,
    }),
  ],
  // 可选:自定义登录页
  pages: {
    signIn: '/login',
  },
  // 可选:回调函数,处理登录后的逻辑
  callbacks: {
    async signIn({ user, account, profile }) {
      // 可以在这里检查用户是否在白名单里
      return true // 返回false会阻止登录
    },
    async session({ session, token }) {
      // 给session添加额外信息
      session.user.id = token.sub
      return session
    },
  },
}

const handler = NextAuth(authOptions)
export { handler as GET, handler as POST }

最后一行export { handler as GET, handler as POST }很重要,App Router需要明确导出HTTP方法的处理函数。

环境变量命名规范

NextAuth.js对环境变量命名有点讲究,特别是v5版本:

必需的环境变量:

# .env.local

# NextAuth配置
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your-secret-key-here

# 或者用新的命名(v5推荐)
AUTH_SECRET=your-secret-key-here

# Google OAuth
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret

# 或者用AUTH_前缀(v5自动识别)
AUTH_GOOGLE_ID=your-google-client-id
AUTH_GOOGLE_SECRET=your-google-client-secret

# GitHub OAuth
GITHUB_ID=your-github-client-id
GITHUB_SECRET=your-github-client-secret

# 或者
AUTH_GITHUB_ID=your-github-client-id
AUTH_GITHUB_SECRET=your-github-client-secret

几个关键点:

  1. NEXTAUTH_URL:你的应用完整URL,开发环境是http://localhost:3000,生产环境必须是你的实际域名(含https)。

  2. NEXTAUTH_SECRET / AUTH_SECRET:用来签名JWT的密钥,必须是随机字符串。生成方式:

openssl rand -base64 32

这个东西千万别泄露,也别提交到Git。如果泄露了,赶紧换一个,不然别人能伪造session。

  1. AUTH_前缀的魔法:NextAuth.js v5会自动识别AUTH_PROVIDER_IDAUTH_PROVIDER_SECRET格式的变量,不用在代码里写process.env.XXX,更方便。

安装和最小化配置

说了这么多,上手很简单:

步骤1:安装

npm install next-auth
# 或
pnpm add next-auth

步骤2:创建API route

创建app/api/auth/[...nextauth]/route.ts,粘贴前面的代码。

步骤3:创建.env.local

NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=运行 openssl rand -base64 32 生成的字符串

步骤4:在前端加个登录按钮

// app/login/page.tsx
'use client'

import { signIn } from 'next-auth/react'

export default function LoginPage() {
  return (
    <div>
      <button onClick={() => signIn('google')}>
        用Google登录
      </button>
      <button onClick={() => signIn('github')}>
        用GitHub登录
      </button>
    </div>
  )
}

步骤5:用Provider包裹应用

如果要在客户端组件里使用useSession(),需要加个Provider:

// app/layout.tsx
import { SessionProvider } from 'next-auth/react'

export default function RootLayout({ children }) {
  return (
    <html>
      <body>
        <SessionProvider>
          {children}
        </SessionProvider>
      </body>
    </html>
  )
}

配置完这些,基本骨架就有了。当然现在点登录按钮会报错,因为还没配置Google和GitHub的OAuth应用,这个接下来讲。

Google登录实战配置

Google Cloud Console配置

Google OAuth配置分两部分:在Google Cloud Console创建OAuth应用,然后在Next.js里对接。

第1步:进入Google Cloud Console

访问 https://console.cloud.google.com,用你的Google账号登录。如果是第一次用,会提示你创建项目,随便起个名字,比如”我的博客”。

第2步:启用Google+ API

虽然Google+早就关了,但OAuth还是依赖这个API。点左侧菜单”APIs & Services” → “Library”,搜索”Google+ API”,点进去,点”Enable”。

第3步:创建OAuth凭据

  • 点左侧”Credentials”
  • 点顶部”Create Credentials” → “OAuth client ID”
  • 如果是第一次,会让你先配置”OAuth consent screen”,填一下应用名称、用户支持邮箱,其他可以先跳过
  • Application type选”Web application”
  • Name随便填,比如”Next.js App”

第4步:配置Redirect URIs(重中之重)

这是最容易出错的地方。Authorized redirect URIs要填两个:

开发环境:

http://localhost:3000/api/auth/callback/google

生产环境(等部署后再加):

https://yourdomain.com/api/auth/callback/google

注意几个细节:

  • http vs https:本地开发用http,生产必须https,Google不允许生产环境用http
  • 端口号:如果你本地跑在3001,就得写localhost:3001,不能省略端口
  • 路径:/api/auth/callback/google一个字母都不能错,多个斜杠或者少个斜杠都不行
  • 不要加查询参数:只需要写到/google,后面的?code=xxx是Google自动加的

配置完点”Create”,会弹出Client ID和Client Secret,复制保存好。

第5步:复制到.env.local

GOOGLE_CLIENT_ID=你的Client ID.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxx

Next.js代码集成

前面NextAuth.js的配置已经加了GoogleProvider,只要环境变量对,就能用了。但我们可以优化一下:

// app/api/auth/[...nextauth]/route.ts
import GoogleProvider from "next-auth/providers/google"

export const authOptions = {
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_CLIENT_ID!,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
      authorization: {
        params: {
          prompt: "consent",
          access_type: "offline",
          response_type: "code"
        }
      }
    }),
  ],
}

这里加了authorization.params:

  • prompt: "consent":每次登录都会显示授权页面,方便测试。生产环境可以去掉,Google会记住用户的选择
  • access_type: "offline":会返回refresh token,用户登录一次后,即使offline也能刷新token(如果你需要长期访问用户数据才加这个)
  • response_type: "code":明确使用authorization code flow

测试登录流程

启动Next.js:

npm run dev

访问 http://localhost:3000/login,点”用Google登录”,会跳转到Google授权页。点”允许”后,应该会回跳到你的应用。

在任意页面获取session:

// app/page.tsx
import { getServerSession } from "next-auth"
import { authOptions } from "./api/auth/[...nextauth]/route"

export default async function Home() {
  const session = await getServerSession(authOptions)

  if (session) {
    return <div>你好, {session.user?.name}</div>
  }

  return <div>未登录</div>
}

或者在客户端组件里:

'use client'
import { useSession } from "next-auth/react"

export default function Profile() {
  const { data: session, status } = useSession()

  if (status === "loading") return <div>加载中...</div>
  if (!session) return <div>未登录</div>

  return (
    <div>
      <img src={session.user?.image} alt="头像" />
      <p>{session.user?.name}</p>
      <p>{session.user?.email}</p>
    </div>
  )
}

常见错误排查

错误1: redirect_uri_mismatch

完整报错:

Error 400: redirect_uri_mismatch
The redirect URI in the request, http://localhost:3000/api/auth/callback/google,
does not match the ones authorized for the OAuth client.

原因:你在Google Console配置的URI和实际callback的URI不完全匹配。

排查步骤:

  1. 打开浏览器开发者工具 → Network标签
  2. 点登录按钮,看跳转到Google时的URL
  3. 找到redirect_uri参数,复制完整的值
  4. 回到Google Console,一字不差地粘贴到Authorized redirect URIs
  5. 注意协议(http/https)、端口、路径、有没有多余的斜杠

错误2: Access blocked: This app’s request is invalid

说明你没配置OAuth consent screen,或者没加测试用户。

解决:

  • 回到Google Console → “OAuth consent screen”
  • User Type选”External”(对外发布)或”Internal”(企业内部)
  • 填写应用信息
  • 如果是External且未发布,需要在”Test users”添加你的测试账号邮箱

错误3: 端口号问题

本地开发如果用localhost:3001跑,但redirect URI配的是localhost:3000,也会报错。

简单办法:统一用3000端口,或者在Google Console多加几个redirect URI(3000、3001、3002都加上)。

错误4: HTTPS要求

生产环境部署后,如果你的域名还是http,Google会拒绝。必须用https。Vercel、Netlify这些平台自动给你https,不用担心。

GitHub登录配置

GitHub OAuth App配置

GitHub的OAuth配置比Google简单,不需要启用API,直接创建OAuth App就行。

第1步:进入Developer settings

登录GitHub,点右上角头像 → Settings → 左侧拉到最底下 → Developer settings → OAuth Apps → New OAuth App。

第2步:填写应用信息

  • Application name:随便填,用户看不到
  • Homepage URL:本地开发填http://localhost:3000,生产填你的域名
  • Authorization callback URL:填http://localhost:3000/api/auth/callback/github

注意GitHub的callback URL比较灵活,可以后期再改,不像Google那么严格。

点Register application,会生成Client ID。再点”Generate a new client secret”,生成Secret(只显示一次,记得保存)。

第3步:复制到.env.local

GITHUB_ID=你的Client ID
GITHUB_SECRET=你的Client Secret

Next.js集成

NextAuth.js配置:

import GitHubProvider from "next-auth/providers/github"

export const authOptions = {
  providers: [
    GitHubProvider({
      clientId: process.env.GITHUB_ID!,
      clientSecret: process.env.GITHUB_SECRET!,
    }),
  ],
}

就这么简单,不需要额外配置。

GitHub vs Google的差异

对比项GoogleGitHub
配置难度中等,需要启用API简单,直接创建
Redirect URI严格,必须完全匹配宽松,支持通配符
HTTPS要求生产环境必须localhost可以http
Scope配置需要明确指定默认就够用
用户信息email、name、picturelogin、email、avatar_url

有一点要注意:GitHub的邮箱可能为null,如果用户设置了邮箱隐私保护。所以代码里要做判断:

const userEmail = session.user?.email || '未提供邮箱'

微信登录配置(国内场景)

微信登录的三种方式

这是最容易搞混的地方,微信有三种登录,适用场景完全不同:

登录方式适用场景要求用户体验
开放平台-网站应用独立网站企业资质、备案域名、HTTPS扫码登录,支持PC
公众号网页授权微信内H5页面认证公众号仅微信浏览器内
企业微信企业内部系统企业微信账号仅企业员工

我们这里讲第一种:开放平台网站应用登录,适合独立的Next.js网站。

微信开放平台配置(门槛较高)

前置条件:

  • 企业营业执照(个人开发者不行)
  • 已备案的域名
  • HTTPS证书

第1步:注册开发者账号

访问 https://open.weixin.qq.com,点”注册”,选择”网站应用开发者”,上传营业执照,等待审核(一般1-2个工作日)。

第2步:创建网站应用

审核通过后,管理中心 → 网站应用 → 创建网站应用,填写:

  • 应用名称
  • 应用简介
  • 应用官网:你的域名(必须已备案)
  • 授权回调域:只填域名,不含协议和路径,如yourdomain.com

注意这里和Google/GitHub不同,微信只需要填域名,不是完整URL。微信会自动匹配该域名下的所有路径。

提交后再等审核(1-7天),通过后会分配AppID和AppSecret。

第3步:环境变量

WECHAT_APP_ID=你的AppID
WECHAT_APP_SECRET=你的AppSecret

NextAuth.js自定义Provider

微信没有内置Provider,需要自己写。好消息是NextAuth.js提供了自定义Provider的机制:

// app/api/auth/[...nextauth]/route.ts

const WeChatProvider = {
  id: "wechat",
  name: "WeChat",
  type: "oauth",
  authorization: {
    url: "https://open.weixin.qq.com/connect/qrconnect",
    params: {
      appid: process.env.WECHAT_APP_ID,
      scope: "snsapi_login",
      response_type: "code",
    },
  },
  token: {
    url: "https://api.weixin.qq.com/sns/oauth2/access_token",
    async request({ params, provider }) {
      const response = await fetch(
        `https://api.weixin.qq.com/sns/oauth2/access_token?appid=${process.env.WECHAT_APP_ID}&secret=${process.env.WECHAT_APP_SECRET}&code=${params.code}&grant_type=authorization_code`
      )
      const tokens = await response.json()
      return { tokens }
    },
  },
  userinfo: {
    url: "https://api.weixin.qq.com/sns/userinfo",
    async request({ tokens }) {
      const response = await fetch(
        `https://api.weixin.qq.com/sns/userinfo?access_token=${tokens.access_token}&openid=${tokens.openid}`
      )
      return await response.json()
    },
  },
  profile(profile) {
    return {
      id: profile.unionid || profile.openid,
      name: profile.nickname,
      email: null, // 微信不提供邮箱
      image: profile.headimgurl,
    }
  },
}

export const authOptions = {
  providers: [
    WeChatProvider,
    // ...其他providers
  ],
}

开发环境调试方案

微信登录最麻烦的是本地调试,因为必须HTTPS且域名已备案。我一般用这两种方案:

方案1:内网穿透(推荐)

使用ngrok或cpolar把本地3000端口映射到公网:

# 使用ngrok
ngrok http 3000

# 或使用cpolar(国内更稳定)
cpolar http 3000

会生成一个临时域名,如https://abc123.ngrok.io,把这个域名配置到微信开放平台的授权回调域。

方案2:使用测试号

微信提供测试号,无需企业资质:

但测试号只能你自己用,其他人扫码会提示”未关注公众号”。

微信登录的独特之处

不同点1:返回openid和unionid

  • openid:用户在当前应用下的唯一标识
  • unionid:用户在同一开放平台账号下所有应用的唯一标识(需要绑定多个应用才有)

建议用unionid做用户标识,如果没有unionid就用openid。

不同点2:不提供邮箱

微信API不返回邮箱,所以profile里email是null。如果你的用户系统需要邮箱,得让用户额外补填。

不同点3:access_token有效期短

Google/GitHub的token一般1小时,微信只有2小时,而且每天刷新次数有限(好像是10次)。所以要妥善处理token刷新逻辑。

总结

从OAuth原理到三大平台的实战配置,我们走了一遍完整的第三方登录流程。

核心要点回顾:

  • OAuth的code和token两步设计是为了安全,code在浏览器明文传输没关系,但secret必须藏在后端
  • NextAuth.js让你专注业务逻辑,不用操心Session管理、CSRF防护这些底层细节
  • Google配置最严格,redirect URI必须完全匹配;GitHub最友好;微信门槛最高但国内必备
  • 微信登录需要企业资质和备案域名,开发阶段可以用内网穿透+测试号调试

实用技巧:

  • openssl rand -base64 32生成NEXTAUTH_SECRET,别手写
  • Google Console多配几个端口的redirect URI,避免端口切换时报错
  • GitHub的邮箱可能为null,记得做空值处理
  • 微信的unionid比openid更适合做跨应用的用户唯一标识

说实话,第三方登录最难的不是写代码,而是理解OAuth的设计思路,以及各平台的配置差异。这篇文章如果能帮你少踩几个坑,我就很开心了。

下次你接到”加个Google登录”的需求,应该不会再配一下午了。

Next.js OAuth登录完整配置流程

从理解OAuth原理到配置Google、GitHub、微信三种登录的完整步骤

⏱️ 预计耗时: 2 小时

  1. 1

    步骤1: 理解OAuth流程(代取快递类比)

    OAuth核心思想:不需要把密码给第三方应用,只需要授权它去OAuth提供者那里拿临时通行证。

    代取快递类比:
    • 你(用户)→ 想登录的人
    • 朋友(Next.js应用)→ 帮你代取快递
    • 快递站(OAuth提供者)→ Google、GitHub、微信
    • 门禁卡(密码)→ 不能给别人
    • 临时通行证(access_token)→ 有时效、有权限限制

    四步流程:
    1. 你给朋友取件码(authorization code)
    2. 朋友拿取件码+身份证去快递站(code+client_secret换access_token)
    3. 快递站核实身份后给包裹(用户信息)
    4. 朋友把包裹交给你(登录成功)

    关键点:
    • 取件码(code)是一次性的,有效期短(通常10分钟)
    • 身份证(client_secret)必须保密,只在服务端使用
    • 临时通行证(access_token)有时效、有权限限制
  2. 2

    步骤2: 配置Google登录

    1. 在Google Cloud Console创建OAuth客户端:
    • 访问 https://console.cloud.google.com
    • 创建项目 → API和服务 → 凭据 → 创建OAuth客户端ID
    • 应用类型:Web应用
    • 授权重定向URI:http://localhost:3000/api/auth/callback/google

    2. 获取client_id和client_secret

    3. 配置NextAuth.js:
    ```ts
    // app/api/auth/[...nextauth]/route.ts
    import NextAuth from 'next-auth'
    import GoogleProvider from 'next-auth/providers/google'

    export const authOptions = {
    providers: [
    GoogleProvider({
    clientId: process.env.GOOGLE_CLIENT_ID!,
    clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
    })
    ],
    }

    const handler = NextAuth(authOptions)
    export { handler as GET, handler as POST }
    ```

    4. 设置环境变量:
    ```
    GOOGLE_CLIENT_ID=你的client_id
    GOOGLE_CLIENT_SECRET=你的client_secret
    NEXTAUTH_URL=http://localhost:3000
    NEXTAUTH_SECRET=随机字符串
    ```

    5. 在页面中使用:
    ```tsx
    import { signIn } from 'next-auth/react'

    <button onClick={() => signIn('google')}>
    使用Google登录
    </button>
    ```
  3. 3

    步骤3: 配置GitHub登录

    1. 在GitHub创建OAuth App:
    • 访问 https://github.com/settings/developers
    • New OAuth App
    • Authorization callback URL:http://localhost:3000/api/auth/callback/github

    2. 获取Client ID和Client Secret

    3. 配置NextAuth.js:
    ```ts
    import GitHubProvider from 'next-auth/providers/github'

    providers: [
    GitHubProvider({
    clientId: process.env.GITHUB_CLIENT_ID!,
    clientSecret: process.env.GITHUB_CLIENT_SECRET!,
    })
    ]
    ```

    4. 设置环境变量:
    ```
    GITHUB_CLIENT_ID=你的client_id
    GITHUB_CLIENT_SECRET=你的client_secret
    ```

    关键点:GitHub的配置流程和Google类似,只是OAuth提供者不同
  4. 4

    步骤4: 配置微信登录(特殊处理)

    1. 在微信开放平台注册应用:
    • 访问 https://open.weixin.qq.com
    • 创建网站应用
    • 获取AppID和AppSecret
    • 需要企业资质

    2. 配置授权回调域名:
    • 格式:yourdomain.com(不要带http://或https://)
    • 需要ICP备案的域名

    3. 配置自定义Provider:
    ```ts
    import WeChatProvider from 'next-auth/providers/wechat'

    providers: [
    WeChatProvider({
    clientId: process.env.WECHAT_CLIENT_ID!,
    clientSecret: process.env.WECHAT_CLIENT_SECRET!,
    })
    ]
    ```

    4. 本地调试用内网穿透:
    • 使用ngrok或frp
    • 配置回调地址为内网穿透地址
    • 测试完成后改为生产地址

    关键点:
    • 微信登录需要企业资质
    • 本地调试用内网穿透
    • unionid比openid更适合做用户唯一标识
  5. 5

    步骤5: 解决常见错误

    错误1:redirect_uri_mismatch
    • 原因:回调地址不匹配
    • 解决:在OAuth提供者后台配置正确的redirect_uri
    • 注意:本地和线上的回调地址都要配置

    错误2:环境变量没设置
    • 检查:.env.local文件是否存在
    • 检查:环境变量名是否正确
    • 检查:Vercel Dashboard中是否配置了环境变量

    错误3:本地测试正常,部署到线上就报错
    • 原因:线上和本地的回调地址不同
    • 解决:在OAuth提供者后台配置生产环境的redirect_uri
    • 格式:https://yourdomain.com/api/auth/callback/google

    安全建议:
    • client_secret必须保密,只在服务端使用
    • 使用state参数防CSRF攻击
    • 验证回调URL的state参数

常见问题

OAuth流程到底是怎么回事?
用代取快递理解OAuth:

场景:你在快递站有个包裹(用户信息),但你现在在公司,没法亲自去拿。你朋友(Next.js应用)说帮你代取。

流程:
1. 你给朋友一个取件码(authorization code)
• 你点击"用Google登录"按钮后,会跳转到Google的授权页面
• 点同意后,Google会生成一个临时的code,通过URL参数传给你的应用

2. 朋友拿着取件码去快递站(code+client_secret换access_token)
• 你的应用后端用code+client_secret去Google那里换access_token
• 但Google还要核实一下,这个朋友是不是你真正信任的人(client_secret)

3. 核实身份后给快递(用户信息)
• Google确认code+client_secret都对,就把包裹(用户信息)给朋友
• 朋友再交给你(登录成功)

关键点:
• 取件码(code)是一次性的,有效期短(通常10分钟)
• 身份证(client_secret)必须保密,只在服务端使用
• 临时通行证(access_token)有时效、有权限限制

优势:你不需要把密码给第三方应用,只需要授权它去OAuth提供者那里拿临时通行证。
redirect_uri_mismatch是什么错误?
错误原因:回调地址不匹配。

OAuth提供者会验证回调地址,如果和配置的不一致,就会报这个错误。

解决方法:
1. 在OAuth提供者后台配置正确的redirect_uri
2. 本地开发:http://localhost:3000/api/auth/callback/google
3. 生产环境:https://yourdomain.com/api/auth/callback/google
4. 注意:本地和线上的回调地址都要配置

常见错误:
• 只配置了本地,没配置生产环境
• 回调地址写错了(多了/少了斜杠)
• 协议不对(http vs https)

检查方法:
• 查看NextAuth.js的默认回调路径:/api/auth/callback/[provider]
• 确认OAuth提供者后台配置的redirect_uri和这个路径完全一致

注意:配置后需要等待几分钟生效。
微信登录为什么最坑?
问题:

1. 需要企业资质
• 个人开发者无法申请
• 需要营业执照等材料
• 审核时间通常1-3个工作日

2. 文档不友好
• 官方文档写得不够清楚
• 错误信息不够明确
• 调试困难

3. 本地调试麻烦
• 需要内网穿透(ngrok或frp)
• 回调地址配置复杂
• 测试环境限制多

4. 配置复杂
• 需要配置自定义Provider
• unionid和openid的区别
• 授权回调域名需要ICP备案

解决方案:
• 使用内网穿透调试
• 配置自定义Provider
• unionid比openid更适合做用户唯一标识
• 耐心等待审核

建议:如果可能,优先用Google或GitHub登录,微信登录作为补充。
如何配置自定义Provider?
微信登录需要自定义Provider:

```ts
import type { OAuthConfig, OAuthUserConfig } from 'next-auth/providers'

function WeChatProvider(options: OAuthUserConfig<WeChatProfile>): OAuthConfig<WeChatProfile> {
return {
id: 'wechat',
name: 'WeChat',
type: 'oauth',
authorization: {
url: 'https://open.weixin.qq.com/connect/qrconnect',
params: {
appid: options.clientId,
redirect_uri: options.callbackUrl,
response_type: 'code',
scope: 'snsapi_login',
state: 'state',
},
},
token: {
url: 'https://api.weixin.qq.com/sns/oauth2/access_token',
},
userinfo: {
url: 'https://api.weixin.qq.com/sns/userinfo',
},
profile(profile) {
return {
id: profile.openid,
name: profile.nickname,
email: null,
image: profile.headimgurl,
}
},
...options,
}
}
```

关键点:
• 配置authorization URL
• 配置token URL
• 配置userinfo URL
• 实现profile函数

注意:微信登录的配置比较复杂,建议参考官方文档或使用现成的Provider。
unionid和openid有什么区别?
openid:
• 用户在当前应用下的唯一标识
• 不同应用的openid不同
• 适合单应用场景

unionid:
• 用户在微信开放平台下的唯一标识
• 同一用户在不同应用下的unionid相同
• 适合多应用场景

选择建议:
• 单应用 → 用openid
• 多应用 → 用unionid

代码示例:
```ts
// 获取unionid
const response = await fetch(
`https://api.weixin.qq.com/sns/userinfo?access_token=${accessToken}&openid=${openid}`
)
const data = await response.json()
const unionid = data.unionid // 用户唯一标识
```

关键点:
• unionid比openid更适合做用户唯一标识
• 需要用户授权才能获取unionid
• 配置微信开放平台才能使用unionid
本地调试微信登录怎么处理?
问题:微信登录需要配置授权回调域名,本地localhost无法配置。

解决方案:使用内网穿透

1. 使用ngrok:
```bash
ngrok http 3000
```

2. 获取公网地址:
```
https://xxxxx.ngrok.io
```

3. 配置回调地址:
• 在微信开放平台配置:https://xxxxx.ngrok.io/api/auth/callback/wechat
• 在.env.local中配置:NEXTAUTH_URL=https://xxxxx.ngrok.io

4. 测试:
• 访问 https://xxxxx.ngrok.io
• 点击微信登录
• 测试流程

注意事项:
• ngrok免费版地址会变,每次重启都要更新
• 生产环境不要用ngrok
• 测试完成后改为生产地址

建议:使用frp等自建内网穿透,地址更稳定。

18 分钟阅读 · 发布于: 2025年12月19日 · 修改于: 2026年1月22日

评论

使用 GitHub 账号登录后即可评论

相关文章