fix: 补齐内网登录异常回传

This commit is contained in:
chenpei 2026-07-17 19:03:54 +08:00
parent 3f56e0280f
commit 192c1f395b
2 changed files with 46 additions and 8 deletions

View File

@ -28,7 +28,7 @@ function isSafeHandoffNonce(value: unknown) {
async function attachInternalSessionHandoff(source: NextResponse, state: unknown) {
const data = await source.clone().json().catch(() => null)
if (!source.ok || !data?.success || data?.needCompleteProfile || !data?.user?.id) {
if (!data) {
return source
}
@ -43,6 +43,24 @@ async function attachInternalSessionHandoff(source: NextResponse, state: unknown
return source
}
// 首次补资料和授权失败都还没有可交接的内网会话,但弹窗必须能把结果送回原内网页面。
// 目标地址只来自服务器保存的受信 handoff,绝不能回退为 postMessage('*')。
if (!source.ok || !data?.success || data?.needCompleteProfile || !data?.user?.id) {
const response = NextResponse.json(
{
...data,
handoffTargetOrigin: handoff.returnOrigin,
handoffNonce: nonce,
},
{
status: source.status,
headers: { 'Cache-Control': 'no-store' },
},
)
appendSetCookies(response, getSetCookies(source))
return response
}
const ticket = await createDingtalkSessionTicket({
userId: String(data.user.id),
redirectPath: handoff.redirectPath,
@ -84,8 +102,12 @@ async function attachInternalSessionHandoff(source: NextResponse, state: unknown
*/
export async function POST(request: NextRequest) {
let state: unknown = ''
try {
const { code, state } = await request.json()
const body = await request.json()
const { code } = body
state = body?.state
if (!code) {
return NextResponse.json(
@ -101,12 +123,13 @@ export async function POST(request: NextRequest) {
return attachInternalSessionHandoff(response, state)
} catch (error: any) {
console.error('钉钉登录失败:', error)
return NextResponse.json(
const response = NextResponse.json(
{
success: false,
message: error.message || '登录失败,请稍后再试',
},
{ status: 500 },
)
return attachInternalSessionHandoff(response, state)
}
}

View File

@ -94,7 +94,13 @@ function DingtalkCallbackContent() {
const data = await response.json()
if (!response.ok || !data.success) {
throw new Error(data.message || '登录失败')
const loginError = new Error(data.message || '登录失败') as Error & {
handoffTargetOrigin?: string
handoffNonce?: string
}
loginError.handoffTargetOrigin = data?.handoffTargetOrigin
loginError.handoffNonce = data?.handoffNonce
throw loginError
}
// 检查是否需要完善信息
@ -113,7 +119,7 @@ function DingtalkCallbackContent() {
userInfo: data.user,
nonce,
},
window.location.origin,
data.handoffTargetOrigin || window.location.origin,
)
window.close()
} else if (window.parent !== window) {
@ -124,7 +130,7 @@ function DingtalkCallbackContent() {
userInfo: data.user,
nonce,
},
window.location.origin,
data.handoffTargetOrigin || window.location.origin,
)
} else {
router.push(`/complete-profile?userInfo=${userInfoParam}`)
@ -195,16 +201,25 @@ function DingtalkCallbackContent() {
setTimeout(() => {
if (window.opener) {
// 新窗口模式:通知打开窗口的父页面
const targetOrigin = error?.handoffTargetOrigin
if (!targetOrigin || error?.handoffNonce !== parseDingtalkLoginState(state).nonce) {
// 若连受信 handoff 都无法确认,保留错误页给用户看,不能关闭弹窗后让内网页面无提示地卡住。
return
}
window.opener.postMessage(
{ type: 'dingtalk_login_error', message: error.message, nonce: parseDingtalkLoginState(state).nonce },
window.location.origin,
targetOrigin,
)
window.close()
} else if (window.parent !== window) {
// iframe模式:通知父窗口(兼容旧代码)
const targetOrigin = error?.handoffTargetOrigin
if (!targetOrigin || error?.handoffNonce !== parseDingtalkLoginState(state).nonce) {
return
}
window.parent.postMessage(
{ type: 'dingtalk_login_error', message: error.message, nonce: parseDingtalkLoginState(state).nonce },
window.location.origin,
targetOrigin,
)
} else {
router.push('/login')