(() => { const originalFetch = window.fetch window.fetch = (input, init = {}) => { const opts = Object.assign({}, init) if (!opts.credentials) opts.credentials = 'include' return originalFetch(input, opts) } const overlayId = 'authOverlay' const ensureOverlay = () => { let ov = document.getElementById(overlayId) if (ov) return ov ov = document.createElement('div') ov.id = overlayId ov.style.position = 'fixed' ov.style.left = '0'; ov.style.top = '0'; ov.style.right = '0'; ov.style.bottom = '0' ov.style.display = 'none' ov.style.alignItems = 'center' ov.style.justifyContent = 'center' ov.style.background = 'rgba(0,0,0,0.6)' ov.style.zIndex = '9999' const panel = document.createElement('div') panel.style.background = '#fff' panel.style.padding = '20px' panel.style.borderRadius = '8px' panel.style.boxShadow = '0 4px 12px rgba(0,0,0,0.2)' const title = document.createElement('div') title.textContent = '鉴权失败,请重新校验' title.style.fontSize = '16px' title.style.marginBottom = '12px' title.style.color = '#333' const debug = document.createElement('pre') debug.id = 'authDebug' debug.style.fontSize = '12px' debug.style.lineHeight = '1.5' debug.style.background = '#f7f7f8' debug.style.border = '1px solid #e3e3e7' debug.style.padding = '10px' debug.style.margin = '0 0 12px 0' debug.style.maxWidth = '520px' debug.style.maxHeight = '240px' debug.style.overflow = 'auto' debug.textContent = '正在收集诊断信息...' const btn = document.createElement('button') btn.textContent = '重新校验' btn.style.padding = '8px 16px' btn.addEventListener('click', async () => { await handshake() }) panel.appendChild(title) panel.appendChild(debug) panel.appendChild(btn) ov.appendChild(panel) document.body.appendChild(ov) return ov } const showOverlay = () => { ensureOverlay().style.display = 'flex' } const hideOverlay = () => { const ov = ensureOverlay(); ov.style.display = 'none' } let cfg = { enableCookieAuth: true, cookieName: 'rebalance_jwt' } const loadConfig = async () => { try { const res = await fetch('/assets/auth_config.json', { cache: 'no-store' }) if (!res.ok) return const data = await res.json() cfg = Object.assign({}, cfg, data || {}) } catch (_) {} } const debugState = { url: '', config: {}, tokenInUrl: false, tokenCall: { tried: false, ok: false, status: 0 }, handshake: { tried: false, ok: false, status: 0 }, server: {} } const removeTokenFromUrl = () => { try { const nu = new URL(window.location.href) nu.searchParams.delete('token') window.history.replaceState(null, document.title, nu.toString()) } catch (_) {} } const handleTokenInUrl = async () => { try { const u = new URL(window.location.href) const token = u.searchParams.get('token') debugState.url = u.toString() debugState.config = { enableCookieAuth: !!cfg.enableCookieAuth, cookieName: String(cfg.cookieName || '') } debugState.tokenInUrl = !!token if (token) { if (cfg.enableCookieAuth) { const r = await fetch(`/auth/token?token=${encodeURIComponent(token)}`, { method: 'GET' }) debugState.tokenCall.tried = true debugState.tokenCall.ok = r.ok debugState.tokenCall.status = r.status || 0 removeTokenFromUrl() return r.ok } else { removeTokenFromUrl() return true } } } catch (_) {} return true } const handshake = async () => { if (!cfg.enableCookieAuth) { hideOverlay(); return true } try { const r = await fetch('/auth/handshake', { cache: 'no-store' }) debugState.handshake.tried = true debugState.handshake.ok = r.ok debugState.handshake.status = r.status || 0 /* try { const dbgRes = await fetch('/auth/debug', { cache: 'no-store' }) try { const body = await dbgRes.json() debugState.server = body } catch (_) { debugState.server = { error: 'debug parse failed', status: dbgRes.status || 0 } } } catch (e) { debugState.server = { error: 'debug request failed' } } */ const box = document.getElementById('authDebug') if (box) { const lines = [] lines.push(`URL: ${debugState.url}`) lines.push(`Config: enableCookieAuth=${debugState.config.enableCookieAuth} cookieName=${debugState.config.cookieName}`) lines.push(`TokenInUrl: ${debugState.tokenInUrl}`) lines.push(`TokenCall: tried=${debugState.tokenCall.tried} ok=${debugState.tokenCall.ok} status=${debugState.tokenCall.status}`) lines.push(`Handshake: tried=${debugState.handshake.tried} ok=${debugState.handshake.ok} status=${debugState.handshake.status}`) if (debugState.server && typeof debugState.server === 'object') { lines.push(`Server.enableAuth: ${debugState.server.enableAuth}`) lines.push(`Server.jwkLoaded: ${debugState.server.jwkLoaded}`) lines.push(`Server.cookieName: ${debugState.server.cookieName}`) lines.push(`Server.cookiePresent: ${debugState.server.cookiePresent}`) lines.push(`Server.verified: ${debugState.server.verified}`) lines.push(`Server.keysCount: ${debugState.server.keysCount}`) try { const ks = Array.isArray(debugState.server.keysSources) ? debugState.server.keysSources : [] lines.push(`Server.keysSources: ${JSON.stringify(ks)}`) } catch (_) {} try { const th = debugState.server.tokenHeader || {} const tp = debugState.server.tokenPayload || {} lines.push(`Server.tokenHeader.alg: ${th.alg || ''}`) lines.push(`Server.tokenPayload.iss: ${tp.iss || ''}`) lines.push(`Server.tokenPayload.aud: ${tp.aud || ''}`) lines.push(`Server.tokenPayload.exp: ${tp.exp || ''}`) } catch (_) {} if (debugState.server.error) { lines.push(`Server.error: ${debugState.server.error}`) } const rq = debugState.server.request || {} lines.push(`Server.request.host: ${rq.host || ''}`) lines.push(`Server.request.path: ${rq.path || ''}`) lines.push(`Server.request.origin: ${rq.origin || ''}`) lines.push(`Server.request.secFetchMode: ${rq.secFetchMode || ''}`) lines.push(`Server.request.forwardedProto: ${rq.forwardedProto || ''}`) lines.push(`Server.request.secure: ${rq.secure}`) lines.push(`Server.request.proto: ${rq.proto || ''}`) } else { lines.push(`Server: ${String(debugState.server)}`) } lines.push(`Expected URLs: /auth/token?token=... , /auth/handshake , /api/...`) box.textContent = lines.join('\n') } if (r.ok) { hideOverlay(); return true } showOverlay(); return false } catch (_) { showOverlay(); return false } } const init = async () => { await loadConfig() await handleTokenInUrl() await handshake() } if (document.readyState === 'complete' || document.readyState === 'interactive') { init() } else { document.addEventListener('DOMContentLoaded', init) } })()