73 lines
2.1 KiB
TypeScript
73 lines
2.1 KiB
TypeScript
import { NextResponse } from "next/server";
|
|
import { getAuthSettings } from "@/lib/db/queries";
|
|
|
|
const normalizeCallbackUrl = (value: string | null) => {
|
|
if (!value) return "/";
|
|
|
|
try {
|
|
const parsed = new URL(value, "https://local.invalid");
|
|
if (parsed.origin !== "https://local.invalid") return "/";
|
|
return `${parsed.pathname}${parsed.search}${parsed.hash}` || "/";
|
|
} catch {
|
|
return "/";
|
|
}
|
|
};
|
|
|
|
const encodeState = (payload: { org: string; callbackUrl: string }) => {
|
|
return `v1.${Buffer.from(JSON.stringify(payload), "utf8").toString(
|
|
"base64url",
|
|
)}`;
|
|
};
|
|
|
|
export async function GET(request: Request) {
|
|
const settings = await getAuthSettings();
|
|
|
|
if (!settings.isDingTalkEnabled) {
|
|
return NextResponse.json(
|
|
{ error: "DingTalk login is not enabled" },
|
|
{ status: 400 },
|
|
);
|
|
}
|
|
|
|
const urlObj = new URL(request.url);
|
|
const orgName = urlObj.searchParams.get("org");
|
|
const organizations = settings.dingTalkOrganizations ?? [];
|
|
|
|
const selectedOrg = orgName
|
|
? organizations.find((org) => org.name === orgName)
|
|
: organizations[0];
|
|
|
|
const appKey = selectedOrg?.appKey ?? settings.dingTalkAppKey;
|
|
|
|
if (!appKey) {
|
|
return NextResponse.json(
|
|
{ error: "DingTalk appKey is not configured" },
|
|
{ status: 400 },
|
|
);
|
|
}
|
|
|
|
// 动态构建回调地址
|
|
// 优先使用 AUTH_URL 环境变量,确保使用正确的公网域名
|
|
const requestUrl = new URL(request.url);
|
|
const baseUrl = process.env.AUTH_URL || requestUrl.origin;
|
|
const redirectUri = `${baseUrl}/dingtalk-callback.html`;
|
|
|
|
// state 中保存组织和登录后的回跳地址,回调接口仍兼容旧的纯组织名 state。
|
|
const state = encodeState({
|
|
org: orgName || selectedOrg?.name || "default",
|
|
callbackUrl: normalizeCallbackUrl(urlObj.searchParams.get("callbackUrl")),
|
|
});
|
|
|
|
// 钉钉 OAuth 2.0 授权地址
|
|
const url =
|
|
`https://login.dingtalk.com/oauth2/auth?` +
|
|
`redirect_uri=${encodeURIComponent(redirectUri)}&` +
|
|
`response_type=code&` +
|
|
`client_id=${encodeURIComponent(appKey)}&` +
|
|
`scope=openid&` +
|
|
`state=${encodeURIComponent(state)}&` +
|
|
`prompt=consent`;
|
|
|
|
return NextResponse.redirect(url);
|
|
}
|