fix: auto-redirect to Keycloak login, skip intermediate screen

Since tOS uses Keycloak as sole auth provider, the intermediate
login page with the manual "Mit Keycloak anmelden" button was
unnecessary. Now unauthenticated users are redirected directly
to Keycloak. The error UI with retry button is preserved for
failed auth attempts (expired session, unauthorized).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-12 13:02:49 +01:00
parent 20d6795694
commit 35cc24c5ec

View File

@@ -3,7 +3,7 @@
import { signIn, useSession } from 'next-auth/react';
import { useRouter, useSearchParams } from 'next/navigation';
import { useTranslations } from 'next-intl';
import { useEffect, useState } from 'react';
import { useEffect, useRef, useState } from 'react';
import { motion } from 'framer-motion';
import { Loader2, KeyRound } from 'lucide-react';
@@ -16,7 +16,7 @@ interface LoginPageProps {
/**
* Login page component
* Handles authentication via Keycloak
* Automatically redirects to Keycloak. Only shows UI on auth errors.
*/
export default function LoginPage({ params: { locale } }: LoginPageProps) {
const { status } = useSession();
@@ -24,6 +24,7 @@ export default function LoginPage({ params: { locale } }: LoginPageProps) {
const searchParams = useSearchParams();
const t = useTranslations('auth');
const [isLoading, setIsLoading] = useState(false);
const autoSignInTriggered = useRef(false);
const callbackUrl = searchParams.get('callbackUrl') || `/${locale}/dashboard`;
const error = searchParams.get('error');
@@ -35,6 +36,14 @@ export default function LoginPage({ params: { locale } }: LoginPageProps) {
}
}, [status, router, callbackUrl]);
// Auto-redirect to Keycloak if no error
useEffect(() => {
if (status === 'unauthenticated' && !error && !autoSignInTriggered.current) {
autoSignInTriggered.current = true;
signIn('keycloak', { callbackUrl });
}
}, [status, error, callbackUrl]);
const handleLogin = async () => {
setIsLoading(true);
try {
@@ -44,8 +53,8 @@ export default function LoginPage({ params: { locale } }: LoginPageProps) {
}
};
// Show loading state while checking session
if (status === 'loading') {
// Show loading spinner during auto-redirect or session check
if (!error) {
return (
<div className="flex min-h-screen items-center justify-center bg-background">
<Loader2 className="h-8 w-8 animate-spin text-primary" />
@@ -53,6 +62,7 @@ export default function LoginPage({ params: { locale } }: LoginPageProps) {
);
}
// Only show full UI when there's an error (so user can retry)
return (
<div className="flex min-h-screen items-center justify-center bg-gradient-to-br from-background to-muted p-4">
<motion.div
@@ -62,7 +72,6 @@ export default function LoginPage({ params: { locale } }: LoginPageProps) {
>
<Card className="w-full max-w-md">
<CardHeader className="space-y-4 text-center">
{/* Logo */}
<div className="mx-auto flex h-16 w-16 items-center justify-center rounded-2xl bg-primary text-primary-foreground">
<span className="text-3xl font-bold">t</span>
<span className="text-3xl font-light">OS</span>
@@ -73,8 +82,6 @@ export default function LoginPage({ params: { locale } }: LoginPageProps) {
</div>
</CardHeader>
<CardContent className="space-y-4">
{/* Error message */}
{error && (
<motion.div
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: 'auto' }}
@@ -82,9 +89,7 @@ export default function LoginPage({ params: { locale } }: LoginPageProps) {
>
{error === 'SessionRequired' ? t('sessionExpired') : t('unauthorized')}
</motion.div>
)}
{/* Login button */}
<Button
onClick={handleLogin}
disabled={isLoading}