如果你的用户群是开发者,GitHub OAuth 是最合适的第二登录选项(甚至是唯一的选项)。它与 Google OAuth 的流程相同——但注册应用只需在 GitHub 上填写一张表单,而不是穿越 Google Cloud Console 的重重页面。更重要的是,用户身份自带其在开发者圈子里积累的信誉。
选择哪些 OAuth 提供商,取决于你的用户是谁。面向普通消费者的应用首选 Google,因为人人都有 Google 账号。B2B 应用首选 Microsoft,因为企业账号在那里。面向开发者的应用则首选 GitHub——它是开发者的职业身份,他们每天都登着那个账号写代码,而"档案里的 GitHub 用户名"所承载的社会认可,是一个普通邮箱地址所没有的。如果你的产品涉及代码、DevOps 或开发者工作流,GitHub OAuth 就是标配。
从技术机制上看,它与 Google 版本走的是同一套 OAuth 2.0 流程:跳转、授权页、带 auth code 的回调、服务端换取 access token、用户查询。差异只在表面。GitHub 的应用注册是 github.com/settings 上的单张表单,没有"审核流程"——创建应用的瞬间,任何 GitHub 用户都可以使用它。GitHub 的用户对象包含用户名(这比邮箱地址更有意义)、头像 URL,以及可选的公开邮箱(默认私密;需要申请 user:email 权限才能获取到可用的值)。
本教程将介绍注册表单的填写、值得申请的权限范围,以及最容易让新手踩坑的地方:如何从响应中拿到一个稳定可用的邮箱地址。
user:email 以及为什么几乎总是需要它进入 GitHub 设置,在左侧边栏底部找到 Developer settings,点击 OAuth Apps → New OAuth App,填写以下信息:
https://yourapp.com/api/auth/callback/github。本地开发时,可以单独注册一个测试应用,或使用 http://localhost:3000/api/auth/callback/github——地址必须完全匹配。点击 Register application,GitHub 会立即显示你的 Client ID。点击 Generate a new client secret 生成密钥;立刻复制保存,它只显示一次。
与 Google 不同,GitHub OAuth Apps 只接受一个回调地址。要同时支持开发环境和生产环境,最简单的方法是注册两个应用:
http://localhost:3000/...https://yourapp.com/...每个应用都有独立的 Client ID 和密钥。开发环境指向应用 #1,生产环境指向应用 #2。两者的名称可以不同——用户在开发环境的授权页上会看到"MyApp (dev)",这对调试来说反而很方便。
https://yourapp.com/auth/callback/github,GitHub 会接受该路径下的任意子路径回调——你可以把状态信息放在路径里。大多数库不会利用这一特性;但在排查"为什么这个 redirect URL 不生效"时,记住这条规则会有帮助。
GITHUB_CLIENT_ID=Iv1.xxxxxxxxx
GITHUB_CLIENT_SECRET=xxxxxxxxxxxxxxxxxxxxxx
在 Auth.js / NextAuth 中,配置方式如下:
import GitHubProvider from "next-auth/providers/github";
export const authOptions = {
providers: [
GitHubProvider({
clientId: process.env.GITHUB_CLIENT_ID,
clientSecret: process.env.GITHUB_CLIENT_SECRET,
}),
],
};
如果你已经配置了 Google 提供商,可以直接把 GitHub 并排加进去——该库支持叠加多个提供商,登录界面会为每个提供商显示对应的按钮。
user:email默认情况下,GitHub OAuth 只授予对用户公开资料的访问权限,而公开资料中可能不包含邮箱地址(大多数用户的主邮箱都设置为私密)。要获取可用的邮箱,需要申请 user:email 权限范围。
Auth.js 默认已经申请了这个权限。手动构建授权 URL 时,需要在查询字符串中加上 &scope=user:email。用户会在授权页上看到相应提示("访问你的邮箱地址")。
其他一些适用于高级场景的权限范围:
read:user——获取超出公开默认值的用户资料信息。repo——读写用户的代码仓库(仅在应用确实需要时申请)。read:org——查看用户所属的组织。原则是:只申请你实际会用到的权限。每增加一个权限范围,授权页的摩擦就多一分,用户的隐私顾虑也随之增加。
授权成功后,GitHub 的 /user 接口返回用户的个人资料。响应中的 email 字段是用户的公开邮箱——很多情况下是 null,因为大多数用户将邮箱设为私密。
要获取可用的邮箱,应该改用 /user/emails 接口。在拥有 user:email 权限的情况下,该接口会返回用户的所有邮箱地址,包括经过验证的主邮箱:
const emailsRes = await fetch('https://api.github.com/user/emails', {
headers: { Authorization: `Bearer ${accessToken}` },
});
const emails = await emailsRes.json();
const primary = emails.find(e => e.primary && e.verified)?.email;
务必选取经过验证的主邮箱,这是 GitHub 自己信任并用于通信的邮箱。Auth.js 会自动处理这一逻辑;手动实现时需要自行处理,而这一步恰恰是最容易被遗漏的。
/user 接口的响应同时包含 login(用户名,例如 octocat)和 id(稳定的数字 ID,例如 583231)。两者都应该存储,但要以 ID 作为外键。
GitHub 允许用户修改用户名;如果以 login 作为关联键,用户改名后你的数据记录就会断链。应以 id 作为关联键,将当前的 login 存储为一个独立字段,并在每次登录时更新。
GitHub 有两种应用类型:
如果只是实现登录功能,OAuth Apps 简单又合适。如果需要与代码交互(创建 PR、发布 Check、访问仓库内容),GitHub Apps 是更现代的方式。两者可以并存——很多产品同时使用 OAuth App 处理登录,以及独立的 GitHub App 处理仓库集成功能。