forked from 77media/video-flow
谷歌登录
This commit is contained in:
parent
6048dd7610
commit
91dc0031d9
@ -86,132 +86,98 @@ export async function POST(request: NextRequest) {
|
|||||||
inviteCode
|
inviteCode
|
||||||
});
|
});
|
||||||
|
|
||||||
// 第一步:使用authorization code向Google换取access token和id_token
|
// 直接将authorization code传递给Java后端处理
|
||||||
const googleClientId = process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID || '847079918888-o1nne8d3ij80dn20qurivo987pv07225.apps.googleusercontent.com';
|
console.log('准备调用Java后端验证Google authorization code...');
|
||||||
const googleClientSecret = process.env.GOOGLE_CLIENT_SECRET || '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';
|
|
||||||
|
|
||||||
if (!googleClientSecret) {
|
// 第二步:调用Java验证接口(只验证不创建用户)
|
||||||
console.error('Google Client Secret未配置');
|
|
||||||
return NextResponse.json(
|
|
||||||
{
|
|
||||||
success: false,
|
|
||||||
message: 'Google Client Secret not configured'
|
|
||||||
},
|
|
||||||
{ status: 500 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('开始向Google交换token...');
|
|
||||||
|
|
||||||
// 创建fetch配置,包含超时和重试机制
|
|
||||||
const fetchOptions = {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/x-www-form-urlencoded',
|
|
||||||
},
|
|
||||||
body: new URLSearchParams({
|
|
||||||
code,
|
|
||||||
client_id: googleClientId,
|
|
||||||
client_secret: googleClientSecret,
|
|
||||||
redirect_uri: getRedirectUri(request),
|
|
||||||
grant_type: 'authorization_code',
|
|
||||||
}),
|
|
||||||
// 增加超时时间到30秒
|
|
||||||
signal: AbortSignal.timeout(30000)
|
|
||||||
};
|
|
||||||
|
|
||||||
let tokenResponse;
|
|
||||||
try {
|
|
||||||
tokenResponse = await fetch('https://oauth2.googleapis.com/token', fetchOptions);
|
|
||||||
} catch (fetchError: any) {
|
|
||||||
console.error('Google API连接失败:', fetchError.message);
|
|
||||||
|
|
||||||
// 如果是超时错误,提供更友好的错误信息
|
|
||||||
if (fetchError.name === 'TimeoutError' || fetchError.code === 'UND_ERR_CONNECT_TIMEOUT') {
|
|
||||||
return NextResponse.json(
|
|
||||||
{
|
|
||||||
success: false,
|
|
||||||
message: 'Google authentication service is temporarily unavailable. Please check your network connection and try again.'
|
|
||||||
},
|
|
||||||
{ status: 503 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw fetchError;
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Google token exchange响应状态:', tokenResponse.status);
|
|
||||||
|
|
||||||
if (!tokenResponse.ok) {
|
|
||||||
const errorText = await tokenResponse.text();
|
|
||||||
console.error('Google token exchange failed:', errorText);
|
|
||||||
return NextResponse.json(
|
|
||||||
{
|
|
||||||
success: false,
|
|
||||||
message: 'Failed to exchange authorization code for tokens'
|
|
||||||
},
|
|
||||||
{ status: 400 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const tokenData = await tokenResponse.json();
|
|
||||||
console.log('Google token exchange成功,获得token');
|
|
||||||
const { id_token } = tokenData;
|
|
||||||
|
|
||||||
if (!id_token) {
|
|
||||||
console.error('No id_token received from Google');
|
|
||||||
return NextResponse.json(
|
|
||||||
{
|
|
||||||
success: false,
|
|
||||||
message: 'No id_token received from Google'
|
|
||||||
},
|
|
||||||
{ status: 400 }
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 第二步:使用id_token调用Java后端
|
|
||||||
const javaBaseUrl = process.env.NEXT_PUBLIC_JAVA_URL || 'https://auth.test.movieflow.ai';
|
const javaBaseUrl = process.env.NEXT_PUBLIC_JAVA_URL || 'https://auth.test.movieflow.ai';
|
||||||
console.log('开始调用Java后端:', javaBaseUrl);
|
console.log('开始调用Java验证接口:', javaBaseUrl);
|
||||||
|
|
||||||
const backendResponse = await fetch(`${javaBaseUrl}/api/auth/google/login`, {
|
const verifyResponse = await fetch(`${javaBaseUrl}/api/auth/google/callback`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
'Accept': 'application/json',
|
'Accept': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
idToken: id_token, // 使用从Google获取的id_token
|
code: code, // Google authorization code
|
||||||
action: 'auto', // 自动判断登录或注册
|
state: state, // state参数
|
||||||
inviteCode: inviteCode || stateData.inviteCode || undefined
|
inviteCode: inviteCode || stateData.inviteCode || undefined,
|
||||||
|
skipUserCreation: true // 🔑 关键:只验证不创建用户
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('Java后端响应状态:', backendResponse.status);
|
console.log('Java验证接口响应状态:', verifyResponse.status);
|
||||||
const backendResult = await backendResponse.json();
|
const verifyResult = await verifyResponse.json();
|
||||||
|
|
||||||
if (!backendResponse.ok || !backendResult.success) {
|
if (!verifyResponse.ok || !verifyResult.success) {
|
||||||
console.error('Java后端处理Google OAuth失败:', backendResult);
|
console.error('Java验证接口处理失败:', verifyResult);
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{
|
{
|
||||||
success: false,
|
success: false,
|
||||||
message: backendResult.message || 'Google authentication failed'
|
message: verifyResult.message || 'Google token verification failed'
|
||||||
},
|
},
|
||||||
{ status: backendResponse.status || 500 }
|
{ status: verifyResponse.status || 500 }
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('Google OAuth认证成功:', {
|
console.log('Google Token验证成功:', {
|
||||||
userId: backendResult.data?.user?.userId,
|
email: verifyResult.data?.email,
|
||||||
email: backendResult.data?.user?.email
|
name: verifyResult.data?.name
|
||||||
});
|
});
|
||||||
|
|
||||||
// 返回成功结果
|
// 第三步:调用Python注册接口进行用户创建和积分发放
|
||||||
|
const smartvideoBaseUrl = process.env.NEXT_PUBLIC_BASE_URL || 'https://77.smartvideo.py.qikongjian.com';
|
||||||
|
console.log('开始调用Python注册接口:', smartvideoBaseUrl);
|
||||||
|
|
||||||
|
const registerResponse = await fetch(`${smartvideoBaseUrl}/api/user_fission/register_with_invite`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
email: verifyResult.data.email,
|
||||||
|
name: verifyResult.data.name,
|
||||||
|
auth_type: 'GOOGLE',
|
||||||
|
google_user_info: verifyResult.data,
|
||||||
|
invite_code: inviteCode || stateData.inviteCode || undefined
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Python注册接口响应状态:', registerResponse.status);
|
||||||
|
const registerResult = await registerResponse.json();
|
||||||
|
|
||||||
|
if (!registerResponse.ok || !registerResult.successful) {
|
||||||
|
console.error('Python注册接口处理失败:', registerResult);
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
message: registerResult.message || 'User registration failed'
|
||||||
|
},
|
||||||
|
{ status: registerResponse.status || 500 }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Google OAuth注册成功:', {
|
||||||
|
userId: registerResult.data?.user_id,
|
||||||
|
email: registerResult.data?.email
|
||||||
|
});
|
||||||
|
|
||||||
|
// 返回成功结果(统一格式)
|
||||||
return NextResponse.json({
|
return NextResponse.json({
|
||||||
success: true,
|
success: true,
|
||||||
data: {
|
data: {
|
||||||
token: backendResult.data.token,
|
token: registerResult.data.token,
|
||||||
user: backendResult.data.userInfo || backendResult.data.user,
|
user: {
|
||||||
message: 'Google authentication successful'
|
userId: registerResult.data.user_id,
|
||||||
|
userName: registerResult.data.name,
|
||||||
|
name: registerResult.data.name,
|
||||||
|
email: registerResult.data.email,
|
||||||
|
authType: registerResult.data.auth_type,
|
||||||
|
isNewUser: true
|
||||||
|
},
|
||||||
|
message: 'Google OAuth registration successful with credit rewards'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -78,12 +78,10 @@ export default function OAuthCallback() {
|
|||||||
console.log('State数据:', stateData);
|
console.log('State数据:', stateData);
|
||||||
console.log('最终使用的邀请码:', finalInviteCode);
|
console.log('最终使用的邀请码:', finalInviteCode);
|
||||||
|
|
||||||
// 调用后端处理授权码 - 直接调用Java后端
|
// 调用Next.js API路由处理授权码(新的两步流程:Java验证 + Python注册)
|
||||||
const JAVA_BASE_URL = process.env.NEXT_PUBLIC_JAVA_URL || 'https://auth.test.movieflow.ai';
|
console.log('调用Next.js API路由:', '/api/auth/google/callback');
|
||||||
|
|
||||||
console.log('调用Java后端OAuth接口:', `${JAVA_BASE_URL}/api/auth/google/callback`);
|
const response = await fetch('/api/auth/google/callback', {
|
||||||
|
|
||||||
const response = await fetch(`${JAVA_BASE_URL}/api/auth/google/callback`, {
|
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Accept': 'application/json',
|
'Accept': 'application/json',
|
||||||
|
|||||||
241
docs/jiekou.md
Normal file
241
docs/jiekou.md
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
# Google OAuth 前端改造指南
|
||||||
|
|
||||||
|
## 📋 改造目标
|
||||||
|
让Google OAuth注册用户也能享受积分发放和邀请奖励功能。
|
||||||
|
|
||||||
|
## 🔄 新的流程设计
|
||||||
|
|
||||||
|
### **改造后流程**:
|
||||||
|
```
|
||||||
|
1. 前端接收Google OAuth回调
|
||||||
|
2. 前端 → Java验证接口(只验证,不创建用户)
|
||||||
|
3. 前端 → Python注册接口(创建用户 + 积分发放)
|
||||||
|
4. 返回完整的用户信息和token
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ 前端修改步骤
|
||||||
|
|
||||||
|
### 📍 修改文件
|
||||||
|
**文件**: `video-flow/app/api/auth/google/callback/route.ts`
|
||||||
|
|
||||||
|
### 🔧 核心修改
|
||||||
|
|
||||||
|
#### **第一步:调用Java验证接口**
|
||||||
|
```typescript
|
||||||
|
// 调用Java callback进行Google Token验证(不创建用户)
|
||||||
|
const javaBaseUrl = process.env.NEXT_PUBLIC_JAVA_URL || 'https://auth.test.movieflow.ai';
|
||||||
|
|
||||||
|
const verifyResponse = await fetch(`${javaBaseUrl}/api/auth/google/callback`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
code: code, // Google authorization code
|
||||||
|
state: state, // state参数
|
||||||
|
inviteCode: inviteCode, // 邀请码
|
||||||
|
skipUserCreation: true // 🔑 关键:只验证不创建用户
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const verifyResult = await verifyResponse.json();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### **第二步:调用Python注册接口**
|
||||||
|
```typescript
|
||||||
|
// 调用Python注册接口进行用户创建和积分发放
|
||||||
|
const smartvideoBaseUrl = process.env.NEXT_PUBLIC_SMARTVIDEO_URL || 'https://smartvideo.test.movieflow.ai';
|
||||||
|
|
||||||
|
const registerResponse = await fetch(`${smartvideoBaseUrl}/api/user_fission/register_with_invite`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Accept': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
email: verifyResult.data.email,
|
||||||
|
name: verifyResult.data.name,
|
||||||
|
auth_type: 'GOOGLE',
|
||||||
|
google_user_info: verifyResult.data,
|
||||||
|
invite_code: inviteCode || stateData.inviteCode || undefined
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const registerResult = await registerResponse.json();
|
||||||
|
```
|
||||||
|
|
||||||
|
#### **第三步:返回统一格式结果**
|
||||||
|
```typescript
|
||||||
|
return NextResponse.json({
|
||||||
|
success: true,
|
||||||
|
data: {
|
||||||
|
token: registerResult.data.token,
|
||||||
|
user: {
|
||||||
|
userId: registerResult.data.user_id,
|
||||||
|
userName: registerResult.data.name,
|
||||||
|
name: registerResult.data.name,
|
||||||
|
email: registerResult.data.email,
|
||||||
|
authType: registerResult.data.auth_type,
|
||||||
|
isNewUser: true
|
||||||
|
},
|
||||||
|
message: 'Google OAuth registration successful with credit rewards'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📚 后端接口文档
|
||||||
|
|
||||||
|
### 🔵 Java验证接口
|
||||||
|
|
||||||
|
#### **接口地址**
|
||||||
|
```
|
||||||
|
POST /api/auth/google/callback
|
||||||
|
```
|
||||||
|
|
||||||
|
#### **请求参数**
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
code: string, // Google authorization code
|
||||||
|
state: string, // state参数(可包含邀请码信息)
|
||||||
|
inviteCode?: string, // 邀请码(可选)
|
||||||
|
skipUserCreation: boolean // true=只验证不创建用户
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### **成功响应**
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
success: true,
|
||||||
|
message: "Google Token验证成功",
|
||||||
|
data: {
|
||||||
|
email: string, // 用户邮箱
|
||||||
|
name: string, // 用户姓名
|
||||||
|
picture: string, // 头像URL
|
||||||
|
googleId: string, // Google用户ID
|
||||||
|
verified: boolean, // 是否已验证
|
||||||
|
inviteCode: string // 邀请码(如果有)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### **错误响应**
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
success: false,
|
||||||
|
error: "TOKEN_EXCHANGE_FAILED" | "INVALID_ID_TOKEN",
|
||||||
|
message: string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🔵 Python注册接口
|
||||||
|
|
||||||
|
#### **接口地址**
|
||||||
|
```
|
||||||
|
POST /api/user_fission/register_with_invite
|
||||||
|
```
|
||||||
|
|
||||||
|
#### **请求参数**
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
email: string, // 用户邮箱
|
||||||
|
name?: string, // 用户姓名(可选)
|
||||||
|
auth_type: "GOOGLE", // 认证类型(固定为GOOGLE)
|
||||||
|
google_user_info: { // Google用户信息(来自Java验证接口)
|
||||||
|
email: string,
|
||||||
|
name: string,
|
||||||
|
picture: string,
|
||||||
|
googleId: string,
|
||||||
|
verified: boolean,
|
||||||
|
inviteCode?: string
|
||||||
|
},
|
||||||
|
invite_code?: string // 邀请码(可选)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### **成功响应**
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
successful: true, // 注意:是 successful 不是 success
|
||||||
|
message: "Google OAuth注册成功",
|
||||||
|
data: {
|
||||||
|
user_id: string, // 用户ID(UUID格式)
|
||||||
|
email: string, // 用户邮箱
|
||||||
|
name: string, // 用户姓名
|
||||||
|
auth_type: "GOOGLE", // 认证类型
|
||||||
|
invite_code?: string, // 邀请码
|
||||||
|
token?: string // 认证token(如果有)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### **错误响应**
|
||||||
|
```typescript
|
||||||
|
{
|
||||||
|
successful: false,
|
||||||
|
message: string, // 错误信息
|
||||||
|
detail?: string // 详细错误信息
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔧 环境变量配置
|
||||||
|
|
||||||
|
确保以下环境变量已配置:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Java认证服务地址
|
||||||
|
NEXT_PUBLIC_JAVA_URL=https://auth.test.movieflow.ai
|
||||||
|
|
||||||
|
# Python积分服务地址
|
||||||
|
NEXT_PUBLIC_SMARTVIDEO_URL=https://smartvideo.test.movieflow.ai
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚠️ 重要注意事项
|
||||||
|
|
||||||
|
### 🔍 **关键变更**
|
||||||
|
1. **接口变更**: `/api/auth/google/login` → `/api/auth/google/callback`
|
||||||
|
2. **参数变更**:
|
||||||
|
- 移除: `idToken`, `action`
|
||||||
|
- 新增: `code`, `state`, `skipUserCreation: true`
|
||||||
|
3. **响应字段**: Python返回 `successful`,Java返回 `success`
|
||||||
|
4. **用户ID字段**: Python返回 `user_id`,前端映射为 `userId`
|
||||||
|
|
||||||
|
### 🚨 **错误处理**
|
||||||
|
- Java验证失败 → 停止流程,返回错误
|
||||||
|
- Python注册失败 → 返回详细错误信息
|
||||||
|
- 网络异常 → 提供用户友好提示
|
||||||
|
|
||||||
|
### 🧪 **测试检查点**
|
||||||
|
- [ ] Google OAuth验证是否成功
|
||||||
|
- [ ] 用户是否正确创建并获得积分
|
||||||
|
- [ ] 邀请码功能是否正常
|
||||||
|
- [ ] 错误处理是否完善
|
||||||
|
- [ ] 前端页面跳转是否正常
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🎯 预期效果
|
||||||
|
|
||||||
|
改造完成后,Google OAuth用户将获得:
|
||||||
|
|
||||||
|
✅ **注册奖励积分**
|
||||||
|
✅ **邀请码奖励功能**
|
||||||
|
✅ **邀请人奖励积分**
|
||||||
|
✅ **统一的用户体验**
|
||||||
|
✅ **完整的积分记录**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*文档版本: v2.0*
|
||||||
|
*更新时间: 2025-01-22*
|
||||||
|
*适用范围: video-flow前端项目*
|
||||||
Loading…
x
Reference in New Issue
Block a user