diff --git a/document/OAUTH_SETUP_GUIDE.md b/document/OAUTH_SETUP_GUIDE.md index 215ba31..cc7a5f4 100644 --- a/document/OAUTH_SETUP_GUIDE.md +++ b/document/OAUTH_SETUP_GUIDE.md @@ -7,6 +7,7 @@ ### 常见错误原因 1. **NEXTAUTH_URL 格式错误** + - 包含多余的引号 - URL 格式不正确 - 协议不匹配(http vs https) @@ -94,6 +95,7 @@ AWS_S3_BUCKET="your-s3-bucket-name" **原因**:NEXTAUTH_URL 包含多余的引号或格式错误 **解决**: + 1. 检查 `.env.production` 文件 2. 确保 NEXTAUTH_URL 格式正确 3. 重启应用 @@ -103,6 +105,7 @@ AWS_S3_BUCKET="your-s3-bucket-name" **原因**:Google OAuth 重定向 URI 未正确配置 **解决**: + 1. 在 Google Cloud Console 中添加正确的重定向 URI 2. 确保 URI 格式为:`https://your-domain.com/api/auth/callback/google` @@ -111,5 +114,6 @@ AWS_S3_BUCKET="your-s3-bucket-name" **原因**:Google OAuth 凭据错误 **解决**: + 1. 检查 GOOGLE_CLIENT_ID 和 GOOGLE_CLIENT_SECRET -2. 确保凭据与 Google Cloud Console 中的配置匹配 \ No newline at end of file +2. 确保凭据与 Google Cloud Console 中的配置匹配 diff --git a/env.example b/env.example index 6e7086c..f4412b2 100644 --- a/env.example +++ b/env.example @@ -5,6 +5,8 @@ DATABASE_URL="file:./dev.db" # 重要:确保 URL 不包含多余的引号,格式应为:https://your-domain.com NEXTAUTH_URL="https://recorder.zyj.best" NEXTAUTH_SECRET="your-nextauth-secret" +# 可选:设置 Cookie 域名(生产环境) +NEXTAUTH_COOKIE_DOMAIN="recorder.zyj.best" # Google OAuth GOOGLE_CLIENT_ID="your-google-client-id" diff --git a/lib/auth.ts b/lib/auth.ts index a889824..d1a94db 100644 --- a/lib/auth.ts +++ b/lib/auth.ts @@ -33,6 +33,17 @@ function getValidatedNextAuthUrl(): string { return cleanUrl; } +// 获取域名用于 Cookie 配置 +function getDomain(): string { + const url = getValidatedNextAuthUrl(); + try { + const urlObj = new URL(url); + return urlObj.hostname; + } catch { + return "recorder.zyj.best"; // 默认域名 + } +} + export const authOptions: AuthOptions = { adapter: PrismaAdapter(prisma), @@ -121,6 +132,68 @@ export const authOptions: AuthOptions = { debug: process.env.NODE_ENV === "development", - // 使用验证后的 URL - url: getValidatedNextAuthUrl(), + // 添加 Cookie 配置 + cookies: { + sessionToken: { + name: `next-auth.session-token`, + options: { + httpOnly: true, + sameSite: "lax", + path: "/", + secure: process.env.NODE_ENV === "production", + domain: process.env.NODE_ENV === "production" ? getDomain() : undefined, + }, + }, + callbackUrl: { + name: `next-auth.callback-url`, + options: { + sameSite: "lax", + path: "/", + secure: process.env.NODE_ENV === "production", + domain: process.env.NODE_ENV === "production" ? getDomain() : undefined, + }, + }, + csrfToken: { + name: `next-auth.csrf-token`, + options: { + httpOnly: true, + sameSite: "lax", + path: "/", + secure: process.env.NODE_ENV === "production", + domain: process.env.NODE_ENV === "production" ? getDomain() : undefined, + }, + }, + pkceCodeVerifier: { + name: `next-auth.pkce.code_verifier`, + options: { + httpOnly: true, + sameSite: "lax", + path: "/", + maxAge: 900, + secure: process.env.NODE_ENV === "production", + domain: process.env.NODE_ENV === "production" ? getDomain() : undefined, + }, + }, + state: { + name: `next-auth.state`, + options: { + httpOnly: true, + sameSite: "lax", + path: "/", + maxAge: 900, + secure: process.env.NODE_ENV === "production", + domain: process.env.NODE_ENV === "production" ? getDomain() : undefined, + }, + }, + nonce: { + name: `next-auth.nonce`, + options: { + httpOnly: true, + sameSite: "lax", + path: "/", + secure: process.env.NODE_ENV === "production", + domain: process.env.NODE_ENV === "production" ? getDomain() : undefined, + }, + }, + }, }; diff --git a/scripts/check-env.js b/scripts/check-env.js index c445517..e8c8eb7 100644 --- a/scripts/check-env.js +++ b/scripts/check-env.js @@ -4,18 +4,22 @@ console.log("=== 环境变量检查 ==="); const requiredVars = [ - 'NEXTAUTH_URL', - 'NEXTAUTH_SECRET', - 'GOOGLE_CLIENT_ID', - 'GOOGLE_CLIENT_SECRET', - 'DATABASE_URL' + "NEXTAUTH_URL", + "NEXTAUTH_SECRET", + "GOOGLE_CLIENT_ID", + "GOOGLE_CLIENT_SECRET", + "DATABASE_URL", ]; console.log("\n必需的环境变量:"); -requiredVars.forEach(varName => { +requiredVars.forEach((varName) => { const value = process.env[varName]; if (value) { - console.log(`✅ ${varName}: ${value.substring(0, 20)}${value.length > 20 ? '...' : ''}`); + console.log( + `✅ ${varName}: ${value.substring(0, 20)}${ + value.length > 20 ? "..." : "" + }` + ); } else { console.log(`❌ ${varName}: 未设置`); } @@ -28,14 +32,14 @@ if (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 格式有效`); @@ -62,4 +66,6 @@ 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 +console.log( + "4. 重定向 URI 格式应为: https://your-domain.com/api/auth/callback/google" +); diff --git a/scripts/oauth-debug.js b/scripts/oauth-debug.js new file mode 100644 index 0000000..c519110 --- /dev/null +++ b/scripts/oauth-debug.js @@ -0,0 +1,64 @@ +#!/usr/bin/env node + +// OAuth 调试脚本 +console.log("=== OAuth 配置调试 ==="); + +// 模拟服务器环境变量 +const envVars = { + NEXTAUTH_URL: "https://recorder.zyj.best", + GOOGLE_CLIENT_ID: + "1060072115182-l5u59vrbs2lmcpg7pnn72bc8h37eolff.apps.googleusercontent.com", + GOOGLE_CLIENT_SECRET: "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", +}; + +console.log("\n=== 环境变量检查 ==="); +Object.entries(envVars).forEach(([key, value]) => { + console.log(`✅ ${key}: ${value.substring(0, 30)}...`); +}); + +console.log("\n=== 重定向 URI 分析 ==="); +const nextAuthUrl = envVars.NEXTAUTH_URL; +const expectedRedirectUri = `${nextAuthUrl}/api/auth/callback/google`; + +console.log(`NEXTAUTH_URL: ${nextAuthUrl}`); +console.log(`预期的重定向 URI: ${expectedRedirectUri}`); + +// 验证 URL 格式 +try { + new URL(nextAuthUrl); + console.log("✅ NEXTAUTH_URL 格式有效"); +} catch (error) { + console.log(`❌ NEXTAUTH_URL 格式无效: ${error.message}`); +} + +try { + new URL(expectedRedirectUri); + console.log("✅ 重定向 URI 格式有效"); +} catch (error) { + console.log(`❌ 重定向 URI 格式无效: ${error.message}`); +} + +console.log("\n=== Google Cloud Console 配置检查 ==="); +console.log("请在 Google Cloud Console 中验证以下配置:"); +console.log("1. 项目 ID: 检查你的 Google Cloud 项目"); +console.log( + "2. OAuth 2.0 客户端 ID: 1060072115182-l5u59vrbs2lmcpg7pnn72bc8h37eolff.apps.googleusercontent.com" +); +console.log("3. 授权重定向 URI 应包含:"); +console.log(` - ${expectedRedirectUri}`); + +console.log("\n=== 常见问题排查 ==="); +console.log("1. 确保 Google Cloud Console 中的重定向 URI 完全匹配"); +console.log("2. 检查是否有额外的空格或引号"); +console.log("3. 确保协议是 https(不是 http)"); +console.log("4. 检查域名是否正确(recorder.zyj.best)"); + +console.log("\n=== 测试步骤 ==="); +console.log("1. 访问: https://recorder.zyj.best/login"); +console.log("2. 点击 '使用 Google 登录'"); +console.log("3. 观察浏览器地址栏的重定向 URL"); +console.log("4. 检查是否与 Google Cloud Console 中的配置匹配"); + +console.log("\n=== 调试命令 ==="); +console.log("在服务器上运行以下命令查看应用日志:"); +console.log("docker logs recorder-app --tail 50");