fix(env): fix env
This commit is contained in:
@@ -1,65 +1,138 @@
|
||||
import { NextResponse } from 'next/server'
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
/**
|
||||
* SSO 回调接口
|
||||
* 接收 sso_code,调用 Mock SSO 验证接口换取用户信息,
|
||||
* 然后将用户信息写入 Cookie。
|
||||
*
|
||||
* OAuth2 授权码流程:用一次性授权 code 向真实后端换取 access_token,
|
||||
* 然后将 token / 用户信息写入 Cookie 建立本地会话。
|
||||
*
|
||||
* ⚠️ client_secret 仅在服务端 env(SSO_CLIENT_SECRET),绝不下发浏览器。
|
||||
*/
|
||||
const BACKEND = (
|
||||
process.env.SSO_BACKEND_API || "http://172.16.115.31:6610"
|
||||
).replace(/\/+$/, "");
|
||||
|
||||
export async function POST(req: Request) {
|
||||
try {
|
||||
const { code } = await req.json()
|
||||
const { code } = await req.json();
|
||||
|
||||
if (!code) {
|
||||
return NextResponse.json({ error: '缺少 code 参数' }, { status: 400 })
|
||||
return NextResponse.json({ error: "缺少 code 参数" }, { status: 400 });
|
||||
}
|
||||
|
||||
// 调用 Mock SSO 的验证接口
|
||||
const ssoUrl = process.env.SSO_URL || 'http://localhost:5501'
|
||||
const verifyRes = await fetch(`${ssoUrl}/api/auth/verify`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ code }),
|
||||
})
|
||||
const clientId = process.env.NEXT_PUBLIC_SSO_APP_ID;
|
||||
const clientSecret = process.env.SSO_CLIENT_SECRET;
|
||||
const tenant = process.env.SSO_TENANT || "cowarobot";
|
||||
|
||||
if (!verifyRes.ok) {
|
||||
const errData = await verifyRes.json().catch(() => ({}))
|
||||
if (!clientId || !clientSecret) {
|
||||
console.error(
|
||||
"[external-app] 缺少 NEXT_PUBLIC_SSO_APP_ID 或 SSO_CLIENT_SECRET",
|
||||
);
|
||||
return NextResponse.json(
|
||||
{ error: errData.error || 'SSO 验证失败' },
|
||||
{ status: 401 }
|
||||
)
|
||||
{ error: "服务端 SSO 配置缺失" },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
|
||||
const ssoData = await verifyRes.json()
|
||||
const { token, refresh_token, user, permissions } = ssoData
|
||||
// 用授权 code 换 access_token(标准 OAuth2)
|
||||
const tokenRes = await fetch(`${BACKEND}/api/v1/basis/sso/access_token`, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
code,
|
||||
grant_type: "authorization_code",
|
||||
tenant,
|
||||
}),
|
||||
});
|
||||
|
||||
// 将用户信息和 token 写入 Cookie
|
||||
const response = NextResponse.json({ success: true })
|
||||
const data = await tokenRes.json().catch(() => ({}));
|
||||
console.log(
|
||||
"[external-app] access_token 响应 status:",
|
||||
tokenRes.status,
|
||||
"| body:",
|
||||
JSON.stringify(data).slice(0, 500),
|
||||
);
|
||||
|
||||
response.cookies.set('user', JSON.stringify(user), {
|
||||
httpOnly: false, // 客户端需要读取用于展示
|
||||
path: '/',
|
||||
maxAge: 86400,
|
||||
})
|
||||
if (!tokenRes.ok) {
|
||||
const message = data?.message || data?.msg || "换 token 失败";
|
||||
console.error(
|
||||
"[external-app] access_token error:",
|
||||
tokenRes.status,
|
||||
message,
|
||||
);
|
||||
return NextResponse.json({ error: message }, { status: 401 });
|
||||
}
|
||||
|
||||
response.cookies.set('token', token, {
|
||||
// 兼容平铺 / 外壳两种响应;字段名兼容 user / user_info(basis 习惯用 user_info)
|
||||
const token = data?.access_token ?? data?.data?.access_token;
|
||||
const refreshToken = data?.refresh_token ?? data?.data?.refresh_token;
|
||||
|
||||
if (!token) {
|
||||
console.error(
|
||||
"[external-app] access_token 响应中无 token:",
|
||||
JSON.stringify(data).slice(0, 300),
|
||||
);
|
||||
return NextResponse.json(
|
||||
{ error: "后端未返回 access_token" },
|
||||
{ status: 502 },
|
||||
);
|
||||
}
|
||||
|
||||
const infoRes = await fetch(
|
||||
`${BACKEND}/api/v1/basis/sso/user_info?access_token=${token}`,
|
||||
{
|
||||
method: "GET",
|
||||
},
|
||||
);
|
||||
|
||||
const infoData = await infoRes.json().catch(() => ({}));
|
||||
|
||||
const user = infoData?.data;
|
||||
console.log(
|
||||
"[external-app] 提取 → token:",
|
||||
!!token,
|
||||
"| refresh_token:",
|
||||
!!refreshToken,
|
||||
"| user:",
|
||||
!!user,
|
||||
user
|
||||
? `(${Object.keys(user).join(",")})`
|
||||
: "(无 — 登录态无法建立,首页会跳回 sso-portal)",
|
||||
);
|
||||
|
||||
// 写入 Cookie 建立会话
|
||||
const response = NextResponse.json({ success: true });
|
||||
|
||||
response.cookies.set("token", token, {
|
||||
httpOnly: true,
|
||||
path: '/',
|
||||
path: "/",
|
||||
maxAge: 86400,
|
||||
})
|
||||
});
|
||||
|
||||
if (refresh_token) {
|
||||
response.cookies.set('refresh_token', refresh_token, {
|
||||
if (refreshToken) {
|
||||
response.cookies.set("refresh_token", refreshToken, {
|
||||
httpOnly: true,
|
||||
path: '/',
|
||||
path: "/",
|
||||
maxAge: 86400 * 7,
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
return response
|
||||
if (user) {
|
||||
response.cookies.set("user", JSON.stringify(user), {
|
||||
httpOnly: false, // 客户端需要读取用于展示
|
||||
path: "/",
|
||||
maxAge: 86400,
|
||||
});
|
||||
}
|
||||
|
||||
return response;
|
||||
} catch (err: any) {
|
||||
console.error("[external-app] callback error:", err?.message);
|
||||
return NextResponse.json(
|
||||
{ error: err?.message || '回调处理失败' },
|
||||
{ status: 500 }
|
||||
)
|
||||
{ error: err?.message || "回调处理失败" },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user