ForcePilot/web/src/utils/oidcAutoStart.js

78 lines
2.2 KiB
JavaScript
Raw Normal View History

const AUTO_START_KEY = 'oidc_auto_start_attempted'
// 对 redirect 参数进行安全校验,防止开放重定向漏洞
// - 必须是字符串类型
// - 必须以 / 开头(本地相对路径)
// - 不能以 // 或 \ 开头(协议相对 URL / 非标准路径)
// 不合法或缺失时返回 "/"
export function sanitizeRedirect(value) {
if (typeof value === 'string' && value.length > 0 && value[0] === '/' && value[1] !== '/' && value[1] !== '\\') {
return value
}
return '/'
}
// 检查是否已经尝试过自动触发 OIDC同一会话内避免无限循环
export function hasAutoStartAttempted() {
return sessionStorage.getItem(AUTO_START_KEY) === '1'
}
// 标记已尝试自动触发
export function markAutoStartAttempted() {
sessionStorage.setItem(AUTO_START_KEY, '1')
}
// 清除自动触发尝试标记OIDC 登录成功后调用)
export function clearAutoStartAttempt() {
sessionStorage.removeItem(AUTO_START_KEY)
}
// 尝试自动触发 OIDC 登录
// config: OIDC 配置对象(由调用方在外部获取)
// getOIDCLoginUrl: 获取登录 URL 的异步函数
// 返回 true 表示已发起跳转caller 不应继续执行后续流程
export async function tryAutoStartOIDC(getOIDCLoginUrl, config) {
// 1. OIDC 配置未就绪或未启用
if (!config || !config.enabled) {
return false
}
// 2. 有 oidc_error 时不再自动触发,避免循环
const params = new URLSearchParams(window.location.search)
if (params.has('oidc_error')) {
return false
}
// 3. 必须有 autostartOidc 参数
if (!params.has('autostartOidc')) {
return false
}
// 4. 同一会话内已尝试过,不再重复
if (hasAutoStartAttempted()) {
return false
}
// 5. 获取 OIDC 登录 URL 并跳转
let loginUrlResp
try {
loginUrlResp = await getOIDCLoginUrl()
} catch {
return false
}
if (!loginUrlResp || !loginUrlResp.login_url) {
return false
}
// 保存当前路径,登录后返回(安全校验)
const redirectPath = sanitizeRedirect(params.get('redirect'))
sessionStorage.setItem('oidc_redirect', redirectPath)
// 标记已尝试,防止下次再自动触发
markAutoStartAttempted()
window.location.href = loginUrlResp.login_url
return true
}