7.6 KiB
7.6 KiB
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 客户端 IDredirect_uri: 授权后的回调 URLscope: 请求的权限范围(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 凭据
- 访问 Google Cloud Console
- 创建或选择一个项目
- 启用 Google+ API
- 创建 OAuth 2.0 客户端 ID
- 配置授权重定向 URI:
https://test.crushlevel.ai/api/auth/google/callback http://localhost:3000/api/auth/google/callback (开发环境) - 复制客户端 ID 到环境变量
后端接口要求
后端需要实现登录接口,接收以下参数:
interface LoginRequest {
appClient: AppClient.Web
deviceCode: string // 设备唯一标识
thirdToken: string // Google 授权码
thirdType: ThirdType.Google // 第三方类型
}
后端需要:
- 使用授权码向 Google 交换 access_token
- 使用 access_token 获取用户信息
- 创建或更新用户账号
- 返回应用的登录 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. 加载状态
- 使用
useLoginHook 的 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 | |
|---|---|---|
| OAuth Provider | Discord | |
| 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 |
扩展建议
1. 添加 Apple 登录
参考 Google 登录的实现,创建:
src/lib/oauth/apple.tssrc/app/(auth)/login/components/AppleButton.tsxsrc/app/api/auth/apple/callback/route.ts
2. 统一 OAuth 处理
可以创建通用的 OAuth Hook:
const useOAuthLogin = (provider: 'google' | 'discord' | 'apple') => {
// 通用的 OAuth 登录逻辑
}
3. 添加登录统计
记录不同登录方式的使用情况,优化用户体验。
相关文档
- Google OAuth 2.0 文档
- Next.js API Routes
- Discord OAuth 实现参考