diff --git a/document/OAUTH_SETUP_GUIDE.md b/document/OAUTH_SETUP_GUIDE.md new file mode 100644 index 0000000..215ba31 --- /dev/null +++ b/document/OAUTH_SETUP_GUIDE.md @@ -0,0 +1,115 @@ +# OAuth 设置指南 + +## 问题诊断 + +如果遇到 `Error 400: invalid_request` 错误,通常是 OAuth 配置问题。 + +### 常见错误原因 + +1. **NEXTAUTH_URL 格式错误** + - 包含多余的引号 + - URL 格式不正确 + - 协议不匹配(http vs https) + +2. **Google OAuth 重定向 URI 配置错误** + - 重定向 URI 未在 Google Cloud Console 中正确配置 + - 重定向 URI 格式不正确 + +## 解决步骤 + +### 1. 检查环境变量 + +在服务器上运行环境变量检查脚本: + +```bash +node scripts/check-env.js +``` + +### 2. 修复 NEXTAUTH_URL + +确保 `.env.production` 文件中的 `NEXTAUTH_URL` 格式正确: + +```bash +# 正确的格式 +NEXTAUTH_URL="https://recorder.zyj.best" + +# 错误的格式(包含多余引号) +NEXTAUTH_URL=""https://recorder.zyj.best"" +``` + +### 3. 配置 Google OAuth + +#### 在 Google Cloud Console 中: + +1. 访问 [Google Cloud Console](https://console.cloud.google.com/) +2. 选择你的项目 +3. 进入 "APIs & Services" > "Credentials" +4. 编辑你的 OAuth 2.0 客户端 ID +5. 在 "Authorized redirect URIs" 中添加: + ``` + https://recorder.zyj.best/api/auth/callback/google + ``` + +### 4. 验证配置 + +重新部署应用并测试: + +```bash +./deploy.sh +``` + +### 5. 调试信息 + +如果问题仍然存在,检查应用日志: + +```bash +docker logs recorder-app +``` + +## 环境变量模板 + +```bash +# Database +DATABASE_URL="file:./prod.db" + +# NextAuth.js +NEXTAUTH_URL="https://recorder.zyj.best" +NEXTAUTH_SECRET="your-secure-secret-here" + +# Google OAuth +GOOGLE_CLIENT_ID="your-google-client-id" +GOOGLE_CLIENT_SECRET="your-google-client-secret" + +# AWS S3 Configuration +AWS_ACCESS_KEY_ID="your-aws-access-key-id" +AWS_SECRET_ACCESS_KEY="your-aws-secret-access-key" +AWS_REGION="us-east-1" +AWS_S3_BUCKET="your-s3-bucket-name" +``` + +## 故障排除 + +### 错误:redirect_uri 格式错误 + +**原因**:NEXTAUTH_URL 包含多余的引号或格式错误 + +**解决**: +1. 检查 `.env.production` 文件 +2. 确保 NEXTAUTH_URL 格式正确 +3. 重启应用 + +### 错误:redirect_uri 不在授权列表中 + +**原因**:Google OAuth 重定向 URI 未正确配置 + +**解决**: +1. 在 Google Cloud Console 中添加正确的重定向 URI +2. 确保 URI 格式为:`https://your-domain.com/api/auth/callback/google` + +### 错误:invalid_client + +**原因**:Google OAuth 凭据错误 + +**解决**: +1. 检查 GOOGLE_CLIENT_ID 和 GOOGLE_CLIENT_SECRET +2. 确保凭据与 Google Cloud Console 中的配置匹配 \ No newline at end of file diff --git a/env.example b/env.example index 503b5d5..6e7086c 100644 --- a/env.example +++ b/env.example @@ -2,7 +2,8 @@ DATABASE_URL="file:./dev.db" # NextAuth.js -NEXTAUTH_URL="http://localhost:3000" +# 重要:确保 URL 不包含多余的引号,格式应为:https://your-domain.com +NEXTAUTH_URL="https://recorder.zyj.best" NEXTAUTH_SECRET="your-nextauth-secret" # Google OAuth diff --git a/lib/auth.ts b/lib/auth.ts index 1e3d30f..a889824 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -7,6 +7,32 @@ import CredentialsProvider from "next-auth/providers/credentials"; import { UserService } from "./services/user.service"; import { AuthOptions } from "next-auth"; +// 验证和清理 NEXTAUTH_URL +function getValidatedNextAuthUrl(): string { + const url = process.env.NEXTAUTH_URL; + if (!url) { + throw new Error("NEXTAUTH_URL 环境变量未设置"); + } + + // 清理 URL,移除多余的引号 + let cleanUrl = url.trim(); + if (cleanUrl.startsWith('"') && cleanUrl.endsWith('"')) { + cleanUrl = cleanUrl.slice(1, -1); + } + if (cleanUrl.startsWith("'") && cleanUrl.endsWith("'")) { + cleanUrl = cleanUrl.slice(1, -1); + } + + // 确保 URL 格式正确 + try { + new URL(cleanUrl); + } catch (error) { + throw new Error(`无效的 NEXTAUTH_URL: ${cleanUrl}`); + } + + return cleanUrl; +} + export const authOptions: AuthOptions = { adapter: PrismaAdapter(prisma), @@ -94,4 +120,7 @@ export const authOptions: AuthOptions = { secret: process.env.NEXTAUTH_SECRET, debug: process.env.NODE_ENV === "development", + + // 使用验证后的 URL + url: getValidatedNextAuthUrl(), }; diff --git a/scripts/check-env.js b/scripts/check-env.js new file mode 100644 index 0000000..c445517 --- /dev/null +++ b/scripts/check-env.js @@ -0,0 +1,65 @@ +#!/usr/bin/env node + +// 环境变量检查脚本 +console.log("=== 环境变量检查 ==="); + +const requiredVars = [ + 'NEXTAUTH_URL', + 'NEXTAUTH_SECRET', + 'GOOGLE_CLIENT_ID', + 'GOOGLE_CLIENT_SECRET', + 'DATABASE_URL' +]; + +console.log("\n必需的环境变量:"); +requiredVars.forEach(varName => { + const value = process.env[varName]; + if (value) { + console.log(`✅ ${varName}: ${value.substring(0, 20)}${value.length > 20 ? '...' : ''}`); + } else { + console.log(`❌ ${varName}: 未设置`); + } +}); + +console.log("\n=== NEXTAUTH_URL 详细检查 ==="); +const nextAuthUrl = process.env.NEXTAUTH_URL; +if (nextAuthUrl) { + console.log(`原始值: "${nextAuthUrl}"`); + console.log(`长度: ${nextAuthUrl.length}`); + console.log(`包含引号: ${nextAuthUrl.includes('"')}`); + console.log(`包含单引号: ${nextAuthUrl.includes("'")}`); + + // 清理 URL + let cleanUrl = nextAuthUrl.trim(); + if (cleanUrl.startsWith('"') && cleanUrl.endsWith('"')) { + cleanUrl = cleanUrl.slice(1, -1); + console.log(`清理后: "${cleanUrl}"`); + } + + try { + new URL(cleanUrl); + console.log(`✅ URL 格式有效`); + } catch (error) { + console.log(`❌ URL 格式无效: ${error.message}`); + } +} else { + console.log("❌ NEXTAUTH_URL 未设置"); +} + +console.log("\n=== Google OAuth 配置检查 ==="); +const googleClientId = process.env.GOOGLE_CLIENT_ID; +const googleClientSecret = process.env.GOOGLE_CLIENT_SECRET; + +if (googleClientId && googleClientSecret) { + console.log("✅ Google OAuth 凭据已设置"); + console.log(`Client ID 长度: ${googleClientId.length}`); + console.log(`Client Secret 长度: ${googleClientSecret.length}`); +} else { + console.log("❌ Google OAuth 凭据未完全设置"); +} + +console.log("\n=== 建议 ==="); +console.log("1. 确保 NEXTAUTH_URL 不包含多余的引号"); +console.log("2. 确保 Google OAuth 重定向 URI 配置正确"); +console.log("3. 在 Google Cloud Console 中添加正确的重定向 URI"); +console.log("4. 重定向 URI 格式应为: https://your-domain.com/api/auth/callback/google"); \ No newline at end of file