crush-level-web/docs/GoogleOAuth.md

290 lines
7.6 KiB
Markdown
Raw Permalink Normal View History

2025-11-13 08:38:25 +00:00
# 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 实现参考