crush-level-web/docs/GoogleOAuth.md

7.6 KiB
Raw Blame History

Google OAuth 登录集成文档

功能概述

实现了 Google OAuth 2.0 登录功能,参考 Discord 登录的实现模式,用户可以通过 Google 账号快速登录应用。

实现架构

1. OAuth 流程

用户点击 "Continue with Google"
    ↓
跳转到 Google 授权页面
    ↓
用户授权后Google 重定向到回调 URL
    ↓
API 路由接收授权码并重定向回登录页
    ↓
前端获取授权码并调用后端登录接口
    ↓
登录成功,跳转到首页或指定页面

文件结构

src/
├── lib/
│   └── oauth/
│       ├── discord.ts          # Discord OAuth 配置
│       └── google.ts           # Google OAuth 配置 (新增)
├── app/
│   ├── (auth)/
│   │   └── login/
│   │       └── components/
│   │           ├── DiscordButton.tsx    # Discord 登录按钮
│   │           ├── GoogleButton.tsx     # Google 登录按钮 (新增)
│   │           └── login-form.tsx       # 登录表单 (已更新)
│   └── api/
│       └── auth/
│           ├── discord/
│           │   └── callback/
│           │       └── route.ts         # Discord 回调路由
│           └── google/
│               └── callback/
│                   └── route.ts         # Google 回调路由 (新增)

核心文件说明

1. Google OAuth 配置 (src/lib/oauth/google.ts)

export const googleOAuth = {
  getAuthUrl: (state?: string): string => {
    // 构建 Google OAuth 授权 URL
    // 包含 client_id, redirect_uri, scope 等参数
  }
}

配置参数:

  • client_id: Google OAuth 客户端 ID
  • redirect_uri: 授权后的回调 URL
  • scope: 请求的权限范围email, profile
  • access_type: offline获取 refresh_token
  • prompt: consent每次都显示授权页面

2. GoogleButton 组件 (src/app/(auth)/login/components/GoogleButton.tsx)

功能:

  • 处理 Google 登录按钮点击事件
  • 生成随机 state 用于安全验证
  • 跳转到 Google 授权页面
  • 处理 OAuth 回调(授权码)
  • 调用后端登录接口
  • 处理登录成功/失败的重定向

关键方法:

const handleGoogleLogin = () => {
  // 1. 生成 state
  const state = Math.random().toString(36).substring(2, 15)
  
  // 2. 获取授权 URL
  const authUrl = googleOAuth.getAuthUrl(state)
  
  // 3. 保存 state 到 sessionStorage
  sessionStorage.setItem('google_oauth_state', state)
  
  // 4. 跳转到 Google 授权页面
  window.location.href = authUrl
}

OAuth 回调处理:

useEffect(() => {
  const googleCode = searchParams.get('google_code')
  const googleState = searchParams.get('google_state')
  
  if (googleCode) {
    // 验证 state
    // 调用后端登录接口
    // 处理登录结果
  }
}, [])

3. Google 回调路由 (src/app/api/auth/google/callback/route.ts)

功能:

  • 接收 Google OAuth 回调
  • 提取授权码 (code) 和 state
  • 重定向回登录页面,并将参数传递给前端
export async function GET(request: NextRequest) {
  const code = searchParams.get('code')
  const state = searchParams.get('state')
  
  // 重定向到登录页,携带 google_code 和 google_state
  redirectUrl.searchParams.set('google_code', code)
  redirectUrl.searchParams.set('google_state', state)
  
  return NextResponse.redirect(redirectUrl)
}

环境变量配置

需要在 .env.local 中添加以下环境变量:

# Google OAuth 配置
NEXT_PUBLIC_GOOGLE_CLIENT_ID=your_google_client_id_here
NEXT_PUBLIC_APP_URL=https://test.crushlevel.ai

获取 Google OAuth 凭据

  1. 访问 Google Cloud Console
  2. 创建或选择一个项目
  3. 启用 Google+ API
  4. 创建 OAuth 2.0 客户端 ID
  5. 配置授权重定向 URI
    https://test.crushlevel.ai/api/auth/google/callback
    http://localhost:3000/api/auth/google/callback (开发环境)
    
  6. 复制客户端 ID 到环境变量

后端接口要求

后端需要实现登录接口,接收以下参数:

interface LoginRequest {
  appClient: AppClient.Web
  deviceCode: string           // 设备唯一标识
  thirdToken: string           // Google 授权码
  thirdType: ThirdType.Google  // 第三方类型
}

后端需要:

  1. 使用授权码向 Google 交换 access_token
  2. 使用 access_token 获取用户信息
  3. 创建或更新用户账号
  4. 返回应用的登录 token

安全特性

1. State 参数验证

  • 前端生成随机 state 并保存到 sessionStorage
  • 回调时验证 state 是否匹配
  • 防止 CSRF 攻击

2. 授权码模式

  • 使用 OAuth 2.0 授权码流程
  • 授权码只能使用一次
  • Token 交换在后端进行,更安全

3. URL 参数清理

  • 登录成功后清理 URL 中的敏感参数
  • 防止参数泄露

用户体验优化

1. 重定向保持

// 保存登录前的页面
sessionStorage.setItem('login_redirect_url', redirect || '')

// 登录成功后跳转回原页面
const loginRedirectUrl = sessionStorage.getItem('login_redirect_url')
if (loginRedirectUrl) {
  router.push(loginRedirectUrl)
}

2. 错误处理

  • 授权失败时显示友好的错误提示
  • 自动清理 URL 参数
  • 不影响用户继续尝试登录

3. 加载状态

  • 使用 useLogin Hook 的 loading 状态
  • 可以添加 loading 动画提升体验

测试清单

本地测试

  • 点击 Google 登录按钮跳转到 Google 授权页面
  • 授权后正确回调到应用
  • 授权码正确传递给后端
  • 登录成功后跳转到首页
  • State 参数验证正常工作
  • 错误情况处理正确

生产环境测试

  • 配置正确的回调 URL
  • HTTPS 证书有效
  • 环境变量配置正确
  • 后端接口正常工作

常见问题

1. 回调 URL 不匹配

错误: redirect_uri_mismatch

解决方案:

  • 检查 Google Cloud Console 中配置的回调 URL
  • 确保 NEXT_PUBLIC_APP_URL 环境变量正确
  • 开发环境和生产环境需要分别配置

2. State 验证失败

错误: "Google login failed"

解决方案:

  • 检查 sessionStorage 是否正常工作
  • 确保没有跨域问题
  • 检查浏览器是否禁用了 cookie/storage

3. 授权码已使用

错误: 后端返回授权码无效

解决方案:

  • 授权码只能使用一次
  • 避免重复调用登录接口
  • 清理 URL 参数防止页面刷新时重复使用

与 Discord 登录的对比

特性 Discord Google
OAuth Provider Discord Google
Scopes identify, email userinfo.email, userinfo.profile
授权 URL discord.com/api/oauth2/authorize accounts.google.com/o/oauth2/v2/auth
回调路由 /api/auth/discord/callback /api/auth/google/callback
URL 参数 discord_code, discord_state google_code, google_state
ThirdType Discord Google

扩展建议

1. 添加 Apple 登录

参考 Google 登录的实现,创建:

  • src/lib/oauth/apple.ts
  • src/app/(auth)/login/components/AppleButton.tsx
  • src/app/api/auth/apple/callback/route.ts

2. 统一 OAuth 处理

可以创建通用的 OAuth Hook

const useOAuthLogin = (provider: 'google' | 'discord' | 'apple') => {
  // 通用的 OAuth 登录逻辑
}

3. 添加登录统计

记录不同登录方式的使用情况,优化用户体验。

相关文档