fix(auth): убрать SSO-цикл на /auth/callback после replaceState
Docker / build (push) Failing after 18s

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Denozordec
2026-07-18 14:09:21 +07:00
co-authored by Cursor
parent 812aa614df
commit f26b2c8777
2 changed files with 19 additions and 8 deletions
+5 -1
View File
@@ -38,9 +38,13 @@ async function fetchApi<T>(path: string, options: RequestInit = {}): Promise<T>
})
if (!res.ok) {
if (res.status === 401) {
// Avoid redirect storms: only hand off once per page load
const handoffKey = 'vps_auth_401_handoff'
const already = sessionStorage.getItem(handoffKey)
clearToken()
const cfg = await ensureAuthConfig()
if (cfg.required || isAuthEnabled()) {
if ((cfg.required || isAuthEnabled()) && !already) {
sessionStorage.setItem(handoffKey, '1')
redirectToPortalLogin(`${window.location.origin}/auth/callback`)
}
}
+14 -7
View File
@@ -2,6 +2,8 @@ import { createFileRoute, redirect } from '@tanstack/react-router'
import {
ensureAuthConfig,
firstAllowedPath,
getClaims,
getToken,
parseHashToken,
redirectToPortalLogin,
setToken,
@@ -11,14 +13,19 @@ export const Route = createFileRoute('/auth/callback')({
beforeLoad: async () => {
await ensureAuthConfig()
const { accessToken } = parseHashToken(window.location.hash)
if (!accessToken) {
redirectToPortalLogin(`${window.location.origin}/auth/callback`)
await new Promise(() => {})
return
if (accessToken) {
setToken(accessToken)
// Do not replaceState to strip the hash here — that re-triggers beforeLoad
// with an empty hash and sends the user back to the portal (SSO loop).
sessionStorage.removeItem('vps_auth_401_handoff')
throw redirect({ to: firstAllowedPath() })
}
setToken(accessToken)
window.history.replaceState(null, '', '/auth/callback')
throw redirect({ to: firstAllowedPath() })
// Already stored from a previous parse (e.g. remount) — finish handoff.
if (getToken() && getClaims()) {
throw redirect({ to: firstAllowedPath() })
}
redirectToPortalLogin(`${window.location.origin}/auth/callback`)
await new Promise(() => {})
},
component: () => null,
})