fix: resolve OAuth redirect_uri error - Add URL validation and cleanup in auth config - Create environment variable check script - Add OAuth setup guide documentation
This commit is contained in:
115
document/OAUTH_SETUP_GUIDE.md
Normal file
115
document/OAUTH_SETUP_GUIDE.md
Normal file
@ -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 中的配置匹配
|
||||||
@ -2,7 +2,8 @@
|
|||||||
DATABASE_URL="file:./dev.db"
|
DATABASE_URL="file:./dev.db"
|
||||||
|
|
||||||
# NextAuth.js
|
# NextAuth.js
|
||||||
NEXTAUTH_URL="http://localhost:3000"
|
# 重要:确保 URL 不包含多余的引号,格式应为:https://your-domain.com
|
||||||
|
NEXTAUTH_URL="https://recorder.zyj.best"
|
||||||
NEXTAUTH_SECRET="your-nextauth-secret"
|
NEXTAUTH_SECRET="your-nextauth-secret"
|
||||||
|
|
||||||
# Google OAuth
|
# Google OAuth
|
||||||
|
|||||||
29
lib/auth.ts
29
lib/auth.ts
@ -7,6 +7,32 @@ import CredentialsProvider from "next-auth/providers/credentials";
|
|||||||
import { UserService } from "./services/user.service";
|
import { UserService } from "./services/user.service";
|
||||||
import { AuthOptions } from "next-auth";
|
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 = {
|
export const authOptions: AuthOptions = {
|
||||||
adapter: PrismaAdapter(prisma),
|
adapter: PrismaAdapter(prisma),
|
||||||
|
|
||||||
@ -94,4 +120,7 @@ export const authOptions: AuthOptions = {
|
|||||||
secret: process.env.NEXTAUTH_SECRET,
|
secret: process.env.NEXTAUTH_SECRET,
|
||||||
|
|
||||||
debug: process.env.NODE_ENV === "development",
|
debug: process.env.NODE_ENV === "development",
|
||||||
|
|
||||||
|
// 使用验证后的 URL
|
||||||
|
url: getValidatedNextAuthUrl(),
|
||||||
};
|
};
|
||||||
|
|||||||
65
scripts/check-env.js
Normal file
65
scripts/check-env.js
Normal file
@ -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");
|
||||||
Reference in New Issue
Block a user