crush-level-web/docs/GoogleOAuth.md

290 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 实现参考