# 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`) ```typescript 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 回调(授权码) - 调用后端登录接口 - 处理登录成功/失败的重定向 **关键方法**: ```typescript 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 回调处理**: ```typescript 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 - 重定向回登录页面,并将参数传递给前端 ```typescript 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` 中添加以下环境变量: ```bash # 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](https://console.cloud.google.com/) 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 到环境变量 ## 后端接口要求 后端需要实现登录接口,接收以下参数: ```typescript 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. 重定向保持 ```typescript // 保存登录前的页面 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: ```typescript const useOAuthLogin = (provider: 'google' | 'discord' | 'apple') => { // 通用的 OAuth 登录逻辑 } ``` ### 3. 添加登录统计 记录不同登录方式的使用情况,优化用户体验。 ## 相关文档 - [Google OAuth 2.0 文档](https://developers.google.com/identity/protocols/oauth2) - [Next.js API Routes](https://nextjs.org/docs/app/building-your-application/routing/route-handlers) - Discord OAuth 实现参考