You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
npmrun d853f2ad25 feat(oauth): 实现 GitHub OAuth2 登录功能 3 days ago
..
API.md feat(oauth): 实现 GitHub OAuth2 登录功能 3 days ago
FLOW.md feat(oauth): 实现 GitHub OAuth2 登录功能 3 days ago
PROVIDERS.md feat(oauth): 实现 GitHub OAuth2 登录功能 3 days ago
README.md feat(oauth): 实现 GitHub OAuth2 登录功能 3 days ago

README.md

OAuth2 使用指南

创建日期:2026-05-26


概述

OAuth2 模块提供基于 OAuth2.0 协议的第三方登录认证功能,支持多种 Provider(如 GitHub、Google),采用模块化设计,易于扩展。

主要功能:

  • 第三方账号登录(新用户自动注册)
  • 已有账号绑定第三方账号
  • 账号解绑
  • 多 Provider 支持

快速开始

1. 配置环境变量

# .env
GITHUB_CLIENT_ID=your_github_client_id
GITHUB_CLIENT_SECRET=your_github_client_secret

2. GitHub 开发者应用申请

  1. 访问 GitHub Developer Settings
  2. 点击 "New OAuth App"
  3. 填写信息:
    • Application name: 你的应用名称
    • Homepage URL: http://localhost:3000
    • Authorization callback URL: http://localhost:3000/api/auth/oauth/github/callback
  4. 点击 "Register application"
  5. 复制 Client ID
  6. 生成 Client Secret

3. 前端集成

登录按钮

在登录页面添加 GitHub 登录按钮:

<template>
  <div class="login-page">
    <!-- 原有登录表单 -->
    <LoginForm />

    <!-- 分割线 -->
    <div class="divider">
      <span></span>
    </div>

    <!-- GitHub 登录按钮 -->
    <button @click="loginWithGithub" class="btn-github">
      <Icon name="github" /> GitHub 登录
    </button>
  </div>
</template>

<script setup>
function loginWithGithub() {
  window.location.href = '/api/auth/oauth/github/authorize';
}
</script>

处理回调

登录页或 Profile 页需要处理回调参数:

<script setup>
const router = useRouter();

onMounted(() => {
  const url = new URL(window.location.href);

  if (url.searchParams.get('oauth_success') === '1') {
    // 刷新用户状态
    refreshSession();
    router.push('/');
  }

  if (url.searchParams.get('bind_success') === '1') {
    // 刷新绑定列表
    fetchBindings();
  }

  if (url.searchParams.get('oauth_error')) {
    const error = url.searchParams.get('oauth_error');
    console.error('OAuth error:', error);
  }
});
</script>

账号绑定(已登录用户)

在用户个人中心页面添加绑定入口:

<script setup>
// 获取绑定列表
const { data: bindingsData } = await useFetch('/api/auth/oauth/bindings');
const bindings = bindingsData.value?.bindings ?? [];

// 解绑操作
async function unbindGithub() {
  await $fetch('/api/auth/oauth/github/unbind', { method: 'DELETE' });
  await refreshBindings();
}
</script>

<template>
  <div class="bindings-list">
    <h3>已绑定的账号</h3>
    <div v-for="binding in bindings" :key="binding.id" class="binding-item">
      <Icon :name="binding.provider" />
      <span>{{ binding.username || binding.email }}</span>
      <button @click="unbind(binding.provider)">解绑</button>
    </div>

    <button @click="loginWithGithub" class="btn-github">
      <Icon name="github" /> 绑定 GitHub
    </button>
  </div>
</template>

API 接口

详细 API 文档请参考 API.md

接口 说明
GET /api/auth/oauth/:provider/authorize 发起 OAuth 授权
GET /api/auth/oauth/:provider/callback Provider 回调处理
POST /api/auth/oauth/bind 绑定 OAuth 账号
DELETE /api/auth/oauth/:provider/unbind 解绑 OAuth 账号
GET /api/auth/oauth/bindings 获取绑定列表
GET /api/auth/oauth/:provider/status 查询绑定状态

流程图详解

详细流程图请参考设计文档 2026-05-26-oauth2-design.md

新用户登录流程

用户点击登录
    ↓
后端生成 state 并重定向到 GitHub
    ↓
用户在 GitHub 授权页面确认
    ↓
GitHub 回调后端 /callback 接口
    ↓
后端验证 state、用 code 换 token、获取用户信息
    ↓
后端查找或创建用户、创建 session
    ↓
重定向到前端页面带上成功参数
    ↓
前端刷新状态显示登录成功

已登录用户绑定流程

用户在个人中心点击"绑定 GitHub"
    ↓
后端生成带 userId 的 binding state
    ↓
重定向到 GitHub 授权页面
    ↓
用户在 GitHub 授权页面确认
    ↓
GitHub 回调后端
    ↓
后端验证 binding state、插入 oauth_accounts 表
    ↓
重定向到个人中心带上成功参数
    ↓
前端刷新绑定列表

安全说明

  1. state 令牌 — 每次授权生成随机 state,防止 CSRF 攻击,5 分钟过期
  2. Token 安全 — access_token 只在后端流转,不暴露给浏览器
  3. Session 安全 — 使用 HttpOnly Cookie,防止 XSS 攻击
  4. 唯一约束provider + provider_user_id 联合唯一索引,防止重复绑定

常见问题

Q: GitHub 登录需要哪些权限?

A: 需要 read:user(获取用户信息)和 user:email(获取邮箱)权限。

Q: 用户邮箱未验证怎么办?

A: GitHub 用户可以设置不公开邮箱,此时 email 字段可能为空。建议引导用户补充邮箱绑定。

Q: 如何添加新的 OAuth Provider?

A: 参考 PROVIDERS.md 文档,只需配置即可添加新 Provider。

Q: OAuth 回调 URL 可以自定义吗?

A: 可以通过 redirect_uri 查询参数传递自定义回调地址,但需该地址已在 Provider 配置的白名单中。


扩展阅读