fix: prevent OIDC redirect loop in oidc-only mode

This commit is contained in:
Xre0uS
2026-03-30 23:38:30 +08:00
parent 533d6f84d8
commit d8f03f6bea

View File

@@ -33,25 +33,12 @@ export default function LoginPage(): React.ReactElement {
const navigate = useNavigate() const navigate = useNavigate()
useEffect(() => { useEffect(() => {
authApi.getAppConfig?.().catch(() => null).then((config: AppConfig | null) => {
if (config) {
setAppConfig(config)
if (!config.has_users) setMode('register')
// Auto-redirect to OIDC if password auth is disabled
if (config.oidc_only_mode && config.oidc_configured && config.has_users) {
const params = new URLSearchParams(window.location.search)
if (!params.get('oidc_code') && !params.get('oidc_error') && !params.get('invite')) {
window.location.href = '/api/auth/oidc/login'
}
}
}
})
// Handle query params (invite token, OIDC callback)
const params = new URLSearchParams(window.location.search) const params = new URLSearchParams(window.location.search)
// Check for invite token in URL (/register?invite=xxx or /login?invite=xxx)
const invite = params.get('invite') const invite = params.get('invite')
const oidcCode = params.get('oidc_code')
const oidcError = params.get('oidc_error')
if (invite) { if (invite) {
setInviteToken(invite) setInviteToken(invite)
setMode('register') setMode('register')
@@ -61,26 +48,27 @@ export default function LoginPage(): React.ReactElement {
setError('Invalid or expired invite link') setError('Invalid or expired invite link')
}) })
window.history.replaceState({}, '', window.location.pathname) window.history.replaceState({}, '', window.location.pathname)
return
} }
// Handle OIDC callback via short-lived auth code (secure exchange)
const oidcCode = params.get('oidc_code')
const oidcError = params.get('oidc_error')
if (oidcCode) { if (oidcCode) {
setIsLoading(true)
window.history.replaceState({}, '', '/login') window.history.replaceState({}, '', '/login')
fetch('/api/auth/oidc/exchange?code=' + encodeURIComponent(oidcCode)) fetch('/api/auth/oidc/exchange?code=' + encodeURIComponent(oidcCode))
.then(r => r.json()) .then(r => r.json())
.then(data => { .then(data => {
if (data.token) { if (data.token) {
localStorage.setItem('auth_token', data.token) localStorage.setItem('auth_token', data.token)
navigate('/dashboard') navigate('/dashboard', { replace: true })
window.location.reload()
} else { } else {
setError(data.error || 'OIDC login failed') setError(data.error || 'OIDC login failed')
} }
}) })
.catch(() => setError('OIDC login failed')) .catch(() => setError('OIDC login failed'))
.finally(() => setIsLoading(false))
return
} }
if (oidcError) { if (oidcError) {
const errorMessages: Record<string, string> = { const errorMessages: Record<string, string> = {
registration_disabled: t('login.oidc.registrationDisabled'), registration_disabled: t('login.oidc.registrationDisabled'),
@@ -90,8 +78,19 @@ export default function LoginPage(): React.ReactElement {
} }
setError(errorMessages[oidcError] || oidcError) setError(errorMessages[oidcError] || oidcError)
window.history.replaceState({}, '', '/login') window.history.replaceState({}, '', '/login')
return
} }
}, [])
authApi.getAppConfig?.().catch(() => null).then((config: AppConfig | null) => {
if (config) {
setAppConfig(config)
if (!config.has_users) setMode('register')
if (config.oidc_only_mode && config.oidc_configured && config.has_users) {
window.location.href = '/api/auth/oidc/login'
}
}
})
}, [navigate, t])
const handleDemoLogin = async (): Promise<void> => { const handleDemoLogin = async (): Promise<void> => {
setError('') setError('')