Ensure auth forms fall back to same-origin account endpoints (#517)

This commit is contained in:
shenlan 2025-10-13 16:33:21 +08:00 committed by GitHub
parent ca0350f94c
commit 13e7a5937b
2 changed files with 103 additions and 18 deletions

View File

@ -61,6 +61,47 @@ export default function LoginContent({ children }: LoginContentProps) {
const loginUrlRef = useRef(loginUrl)
const deriveSameOriginLoginFallback = useCallback((url: string): string | undefined => {
if (typeof window === 'undefined') {
return undefined
}
try {
const currentOrigin = window.location.origin
const parsed = new URL(url, currentOrigin)
if (parsed.origin === currentOrigin) {
const relative = `${parsed.pathname}${parsed.search}${parsed.hash}` || '/api/auth/login'
return relative
}
const localHostnames = new Set(['localhost', '127.0.0.1', '[::1]'])
const parsedHostname = parsed.hostname.toLowerCase()
const browserHostname = window.location.hostname.toLowerCase()
const parsedIsLocal = localHostnames.has(parsedHostname)
const browserIsLocal = localHostnames.has(browserHostname)
if (!browserIsLocal && parsedIsLocal) {
const relative = `${parsed.pathname}${parsed.search}${parsed.hash}` || '/api/auth/login'
return relative
}
if (
window.location.protocol === 'https:' &&
parsed.protocol === 'http:' &&
parsedHostname === browserHostname
) {
parsed.protocol = 'https:'
return parsed.toString()
}
} catch (error) {
console.warn('Failed to derive same-origin login fallback', error)
}
return undefined
}, [])
useEffect(() => {
loginUrlRef.current = loginUrl
}, [loginUrl])
@ -148,20 +189,32 @@ export default function LoginContent({ children }: LoginContentProps) {
try {
response = await fetch(usedUrl, requestPayload)
} catch (primaryError) {
const httpsPattern = /^https:/i
if (httpsPattern.test(usedUrl)) {
const insecureUrl = usedUrl.replace(httpsPattern, 'http:')
const sameOriginFallback = deriveSameOriginLoginFallback(usedUrl)
if (sameOriginFallback && sameOriginFallback !== usedUrl) {
try {
response = await fetch(insecureUrl, requestPayload)
loginUrlRef.current = insecureUrl
usedUrl = insecureUrl
response = await fetch(sameOriginFallback, requestPayload)
loginUrlRef.current = sameOriginFallback
usedUrl = sameOriginFallback
} catch (fallbackError) {
console.error('Primary login request failed, insecure fallback also failed', fallbackError)
console.error('Primary login request failed, same-origin fallback also failed', fallbackError)
throw fallbackError
}
} else {
throw primaryError
const httpsPattern = /^https:/i
if (httpsPattern.test(usedUrl)) {
const insecureUrl = usedUrl.replace(httpsPattern, 'http:')
try {
response = await fetch(insecureUrl, requestPayload)
loginUrlRef.current = insecureUrl
usedUrl = insecureUrl
} catch (fallbackError) {
console.error('Primary login request failed, insecure fallback also failed', fallbackError)
throw fallbackError
}
} else {
throw primaryError
}
}
}

View File

@ -118,6 +118,26 @@ function coerceRegisterUrlOverride(rawValue: string | undefined | null, accountS
}
}
function deriveSameOriginFallback(url: string): string | undefined {
if (typeof window === 'undefined') {
return undefined
}
try {
const currentOrigin = window.location.origin
const parsed = new URL(url, currentOrigin)
const preferred = preferSameOrigin(ensureHttpsForSameHost(parsed.toString()))
if (preferred !== url) {
return preferred
}
} catch (error) {
console.warn('Failed to derive same-origin fallback for register URL', error)
}
return undefined
}
export default function RegisterContent() {
const { language } = useLanguage()
const t = translations[language].auth.register
@ -257,20 +277,32 @@ export default function RegisterContent() {
try {
response = await fetch(usedUrl, requestPayload)
} catch (primaryError) {
const httpsPattern = /^https:/i
if (httpsPattern.test(usedUrl)) {
const insecureUrl = usedUrl.replace(httpsPattern, 'http:')
const sameOriginFallback = deriveSameOriginFallback(usedUrl)
if (sameOriginFallback && sameOriginFallback !== usedUrl) {
try {
response = await fetch(insecureUrl, requestPayload)
registerUrlRef.current = insecureUrl
usedUrl = insecureUrl
response = await fetch(sameOriginFallback, requestPayload)
registerUrlRef.current = sameOriginFallback
usedUrl = sameOriginFallback
} catch (fallbackError) {
console.error('Primary register request failed, insecure fallback also failed', fallbackError)
console.error('Primary register request failed, same-origin fallback also failed', fallbackError)
throw fallbackError
}
} else {
throw primaryError
const httpsPattern = /^https:/i
if (httpsPattern.test(usedUrl)) {
const insecureUrl = usedUrl.replace(httpsPattern, 'http:')
try {
response = await fetch(insecureUrl, requestPayload)
registerUrlRef.current = insecureUrl
usedUrl = insecureUrl
} catch (fallbackError) {
console.error('Primary register request failed, insecure fallback also failed', fallbackError)
throw fallbackError
}
} else {
throw primaryError
}
}
}