fix: resolve OAuth state cookie error - Add proper Cookie configuration with domain settings - Add domain extraction function for production environment - Update environment variables example
This commit is contained in:
@ -7,6 +7,7 @@
|
|||||||
### 常见错误原因
|
### 常见错误原因
|
||||||
|
|
||||||
1. **NEXTAUTH_URL 格式错误**
|
1. **NEXTAUTH_URL 格式错误**
|
||||||
|
|
||||||
- 包含多余的引号
|
- 包含多余的引号
|
||||||
- URL 格式不正确
|
- URL 格式不正确
|
||||||
- 协议不匹配(http vs https)
|
- 协议不匹配(http vs https)
|
||||||
@ -94,6 +95,7 @@ AWS_S3_BUCKET="your-s3-bucket-name"
|
|||||||
**原因**:NEXTAUTH_URL 包含多余的引号或格式错误
|
**原因**:NEXTAUTH_URL 包含多余的引号或格式错误
|
||||||
|
|
||||||
**解决**:
|
**解决**:
|
||||||
|
|
||||||
1. 检查 `.env.production` 文件
|
1. 检查 `.env.production` 文件
|
||||||
2. 确保 NEXTAUTH_URL 格式正确
|
2. 确保 NEXTAUTH_URL 格式正确
|
||||||
3. 重启应用
|
3. 重启应用
|
||||||
@ -103,6 +105,7 @@ AWS_S3_BUCKET="your-s3-bucket-name"
|
|||||||
**原因**:Google OAuth 重定向 URI 未正确配置
|
**原因**:Google OAuth 重定向 URI 未正确配置
|
||||||
|
|
||||||
**解决**:
|
**解决**:
|
||||||
|
|
||||||
1. 在 Google Cloud Console 中添加正确的重定向 URI
|
1. 在 Google Cloud Console 中添加正确的重定向 URI
|
||||||
2. 确保 URI 格式为:`https://your-domain.com/api/auth/callback/google`
|
2. 确保 URI 格式为:`https://your-domain.com/api/auth/callback/google`
|
||||||
|
|
||||||
@ -111,5 +114,6 @@ AWS_S3_BUCKET="your-s3-bucket-name"
|
|||||||
**原因**:Google OAuth 凭据错误
|
**原因**:Google OAuth 凭据错误
|
||||||
|
|
||||||
**解决**:
|
**解决**:
|
||||||
|
|
||||||
1. 检查 GOOGLE_CLIENT_ID 和 GOOGLE_CLIENT_SECRET
|
1. 检查 GOOGLE_CLIENT_ID 和 GOOGLE_CLIENT_SECRET
|
||||||
2. 确保凭据与 Google Cloud Console 中的配置匹配
|
2. 确保凭据与 Google Cloud Console 中的配置匹配
|
||||||
@ -5,6 +5,8 @@ DATABASE_URL="file:./dev.db"
|
|||||||
# 重要:确保 URL 不包含多余的引号,格式应为:https://your-domain.com
|
# 重要:确保 URL 不包含多余的引号,格式应为:https://your-domain.com
|
||||||
NEXTAUTH_URL="https://recorder.zyj.best"
|
NEXTAUTH_URL="https://recorder.zyj.best"
|
||||||
NEXTAUTH_SECRET="your-nextauth-secret"
|
NEXTAUTH_SECRET="your-nextauth-secret"
|
||||||
|
# 可选:设置 Cookie 域名(生产环境)
|
||||||
|
NEXTAUTH_COOKIE_DOMAIN="recorder.zyj.best"
|
||||||
|
|
||||||
# Google OAuth
|
# Google OAuth
|
||||||
GOOGLE_CLIENT_ID="your-google-client-id"
|
GOOGLE_CLIENT_ID="your-google-client-id"
|
||||||
|
|||||||
77
lib/auth.ts
77
lib/auth.ts
@ -33,6 +33,17 @@ function getValidatedNextAuthUrl(): string {
|
|||||||
return cleanUrl;
|
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 = {
|
export const authOptions: AuthOptions = {
|
||||||
adapter: PrismaAdapter(prisma),
|
adapter: PrismaAdapter(prisma),
|
||||||
|
|
||||||
@ -121,6 +132,68 @@ export const authOptions: AuthOptions = {
|
|||||||
|
|
||||||
debug: process.env.NODE_ENV === "development",
|
debug: process.env.NODE_ENV === "development",
|
||||||
|
|
||||||
// 使用验证后的 URL
|
// 添加 Cookie 配置
|
||||||
url: getValidatedNextAuthUrl(),
|
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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -4,18 +4,22 @@
|
|||||||
console.log("=== 环境变量检查 ===");
|
console.log("=== 环境变量检查 ===");
|
||||||
|
|
||||||
const requiredVars = [
|
const requiredVars = [
|
||||||
'NEXTAUTH_URL',
|
"NEXTAUTH_URL",
|
||||||
'NEXTAUTH_SECRET',
|
"NEXTAUTH_SECRET",
|
||||||
'GOOGLE_CLIENT_ID',
|
"GOOGLE_CLIENT_ID",
|
||||||
'GOOGLE_CLIENT_SECRET',
|
"GOOGLE_CLIENT_SECRET",
|
||||||
'DATABASE_URL'
|
"DATABASE_URL",
|
||||||
];
|
];
|
||||||
|
|
||||||
console.log("\n必需的环境变量:");
|
console.log("\n必需的环境变量:");
|
||||||
requiredVars.forEach(varName => {
|
requiredVars.forEach((varName) => {
|
||||||
const value = process.env[varName];
|
const value = process.env[varName];
|
||||||
if (value) {
|
if (value) {
|
||||||
console.log(`✅ ${varName}: ${value.substring(0, 20)}${value.length > 20 ? '...' : ''}`);
|
console.log(
|
||||||
|
`✅ ${varName}: ${value.substring(0, 20)}${
|
||||||
|
value.length > 20 ? "..." : ""
|
||||||
|
}`
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
console.log(`❌ ${varName}: 未设置`);
|
console.log(`❌ ${varName}: 未设置`);
|
||||||
}
|
}
|
||||||
@ -62,4 +66,6 @@ console.log("\n=== 建议 ===");
|
|||||||
console.log("1. 确保 NEXTAUTH_URL 不包含多余的引号");
|
console.log("1. 确保 NEXTAUTH_URL 不包含多余的引号");
|
||||||
console.log("2. 确保 Google OAuth 重定向 URI 配置正确");
|
console.log("2. 确保 Google OAuth 重定向 URI 配置正确");
|
||||||
console.log("3. 在 Google Cloud Console 中添加正确的重定向 URI");
|
console.log("3. 在 Google Cloud Console 中添加正确的重定向 URI");
|
||||||
console.log("4. 重定向 URI 格式应为: https://your-domain.com/api/auth/callback/google");
|
console.log(
|
||||||
|
"4. 重定向 URI 格式应为: https://your-domain.com/api/auth/callback/google"
|
||||||
|
);
|
||||||
|
|||||||
64
scripts/oauth-debug.js
Normal file
64
scripts/oauth-debug.js
Normal file
@ -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");
|
||||||
Reference in New Issue
Block a user