fix: prevent logout loop by showing signed-out state instead of auto-redirecting
All checks were successful
CI / build (push) Successful in 2m45s
CI / docker (push) Successful in 1m50s

After logout, redirect to /platform/login?signed_out which shows a
"Signed out" card with a "Sign in again" button instead of immediately
redirecting back to Logto OIDC (which would auto-authenticate if the
Logto session cookie persists).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-25 18:52:26 +02:00
parent 37668dcfe0
commit 2cb818ec71
2 changed files with 39 additions and 4 deletions

View File

@@ -1,13 +1,20 @@
import { useEffect, useRef } from 'react';
import { useEffect, useRef, useState } from 'react';
import { useLogto } from '@logto/react';
import { useNavigate } from 'react-router';
import { Spinner } from '@cameleer/design-system';
import { Button, Card, Spinner } from '@cameleer/design-system';
import cameleerLogo from '@cameleer/design-system/assets/cameleer-logo.svg';
export function LoginPage() {
const { signIn, isAuthenticated, isLoading } = useLogto();
const navigate = useNavigate();
const redirected = useRef(false);
// Check if we arrived here from a logout redirect
const [signedOut] = useState(() => {
const params = new URLSearchParams(window.location.search);
return params.has('signed_out');
});
useEffect(() => {
if (isAuthenticated) {
navigate('/', { replace: true });
@@ -15,11 +22,39 @@ export function LoginPage() {
}, [isAuthenticated, navigate]);
useEffect(() => {
// Don't auto-redirect after logout — show the signed-out state instead
if (signedOut) return;
if (!isLoading && !isAuthenticated && !redirected.current) {
redirected.current = true;
signIn(`${window.location.origin}/platform/callback`);
}
}, [isLoading, isAuthenticated, signIn]);
}, [isLoading, isAuthenticated, signIn, signedOut]);
if (signedOut && !isAuthenticated) {
return (
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '100vh' }}>
<div style={{ maxWidth: 360, textAlign: 'center' }}>
<Card>
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 16 }}>
<img src={cameleerLogo} alt="" width="48" height="48" />
<h2 style={{ margin: 0, fontSize: '1.25rem', fontWeight: 600 }}>Signed out</h2>
<p style={{ margin: 0, color: 'var(--text-secondary)', fontSize: '0.875rem' }}>
You have been signed out successfully.
</p>
<Button
variant="primary"
onClick={() => {
window.location.href = window.location.origin + '/platform/login';
}}
>
Sign in again
</Button>
</div>
</Card>
</div>
</div>
);
}
return (
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', minHeight: '100vh' }}>

View File

@@ -7,7 +7,7 @@ export function useAuth() {
const { currentTenantId } = useOrgStore();
const logout = useCallback(() => {
signOut(window.location.origin + '/platform/login');
signOut(window.location.origin + '/platform/login?signed_out');
}, [signOut]);
return {