From 7a8b736241e55bd48fd54b6fc288a4e5c2e85bfc Mon Sep 17 00:00:00 2001 From: alma Date: Fri, 2 Jan 2026 14:53:29 +0100 Subject: [PATCH] keycloak improve with build 2 --- app/signin/page.tsx | 86 +++++++++++++++++++++++++++-- components/auth/signout-handler.tsx | 13 +++-- components/main-nav.tsx | 13 +++-- 3 files changed, 96 insertions(+), 16 deletions(-) diff --git a/app/signin/page.tsx b/app/signin/page.tsx index fcbbc272..58d03e7f 100644 --- a/app/signin/page.tsx +++ b/app/signin/page.tsx @@ -1,13 +1,48 @@ "use client"; import { signIn, useSession } from "next-auth/react"; -import { useEffect, useState } from "react"; -import { useRouter } from "next/navigation"; +import { useEffect, useState, useRef } from "react"; +import { useRouter, useSearchParams } from "next/navigation"; export default function SignIn() { const { data: session, status } = useSession(); const router = useRouter(); + const searchParams = useSearchParams(); const [initializationStatus, setInitializationStatus] = useState(null); + const hasAttemptedLogin = useRef(false); + const isLogoutRedirect = useRef(false); + + // Check if this is a logout redirect (from Keycloak post_logout_redirect_uri) + useEffect(() => { + // Check URL parameters or session storage for logout flag + const logoutParam = searchParams.get('logout'); + const fromLogout = sessionStorage.getItem('just_logged_out'); + + if (logoutParam === 'true' || fromLogout === 'true') { + isLogoutRedirect.current = true; + sessionStorage.removeItem('just_logged_out'); + + // Clear any OAuth parameters from URL to prevent callback processing + const url = new URL(window.location.href); + const hasOAuthParams = url.searchParams.has('code') || + url.searchParams.has('state') || + url.searchParams.has('error'); + + if (hasOAuthParams) { + // Remove OAuth parameters but keep logout=true + url.searchParams.delete('code'); + url.searchParams.delete('state'); + url.searchParams.delete('error'); + url.searchParams.delete('error_description'); + url.searchParams.set('logout', 'true'); + // Replace URL without OAuth params + window.history.replaceState({}, '', url.toString()); + } + + // Don't auto-trigger login after logout + return; + } + }, [searchParams]); useEffect(() => { // If user is already authenticated, redirect to home @@ -16,10 +51,30 @@ export default function SignIn() { return; } - // Only trigger Keycloak sign-in if not authenticated and not loading + // Don't auto-login if this is a logout redirect or we've already attempted login + if (isLogoutRedirect.current || hasAttemptedLogin.current) { + return; + } + + // Don't auto-login if status is still loading (might be processing OAuth callback) + if (status === "loading") { + return; + } + + // Only trigger Keycloak sign-in if not authenticated, not loading, and not from logout + // Add a longer delay to ensure OAuth callbacks have completed if (status === "unauthenticated") { - // Trigger Keycloak sign-in - signIn("keycloak", { callbackUrl: "/" }); + hasAttemptedLogin.current = true; + // Longer delay to ensure we're not in a logout redirect flow or OAuth callback + const timer = setTimeout(() => { + // Double-check we're still unauthenticated and not in a logout flow + if (!isLogoutRedirect.current) { + // Trigger Keycloak sign-in + signIn("keycloak", { callbackUrl: "/" }); + } + }, 1000); + + return () => clearTimeout(timer); } }, [status, session, router]); @@ -57,6 +112,9 @@ export default function SignIn() { } }, [session]); + // Show logout message if coming from logout + const showLogoutMessage = isLogoutRedirect.current || searchParams.get('logout') === 'true'; + return (

- {initializationStatus === "initializing" + {showLogoutMessage + ? "Vous avez été déconnecté avec succès" + : initializationStatus === "initializing" ? "Initialisation de votre espace..." : initializationStatus === "success" ? "Initialisation réussie, redirection..." @@ -78,6 +138,20 @@ export default function SignIn() { ? "Échec de l'initialisation. Veuillez réessayer." : "Redirection vers la page de connexion..."}

+ {showLogoutMessage && ( +
+ +
+ )} {initializationStatus === "initializing" && (
diff --git a/components/auth/signout-handler.tsx b/components/auth/signout-handler.tsx index 282fd730..43dfc055 100644 --- a/components/auth/signout-handler.tsx +++ b/components/auth/signout-handler.tsx @@ -10,13 +10,16 @@ export function SignOutHandler() { useEffect(() => { const handleSignOut = async () => { try { + // Mark that we're logging out to prevent auto-login + sessionStorage.setItem('just_logged_out', 'true'); + // Get Keycloak issuer from environment const keycloakIssuer = process.env.NEXT_PUBLIC_KEYCLOAK_ISSUER; const idToken = session?.idToken; // First, sign out from NextAuth (clears NextAuth cookies) await signOut({ - callbackUrl: "/signin", + callbackUrl: "/signin?logout=true", redirect: false }); @@ -29,10 +32,10 @@ export function SignOutHandler() { `${keycloakIssuer}/protocol/openid-connect/logout` ); - // Add required parameters + // Add required parameters - include logout=true in redirect URI keycloakLogoutUrl.searchParams.append( 'post_logout_redirect_uri', - window.location.origin + '/signin' + window.location.origin + '/signin?logout=true' ); keycloakLogoutUrl.searchParams.append( 'id_token_hint', @@ -43,12 +46,12 @@ export function SignOutHandler() { window.location.href = keycloakLogoutUrl.toString(); } else { // Fallback: just redirect to signin if we don't have Keycloak info - window.location.href = '/signin'; + window.location.href = '/signin?logout=true'; } } catch (error) { console.error('Error during sign out:', error); // Fallback: redirect to signin on error - window.location.href = '/signin'; + window.location.href = '/signin?logout=true'; } }; diff --git a/components/main-nav.tsx b/components/main-nav.tsx index 912e79ba..acdde8a1 100644 --- a/components/main-nav.tsx +++ b/components/main-nav.tsx @@ -362,12 +362,15 @@ export function MainNav() { className="text-white/80 hover:text-white hover:bg-black/50 cursor-pointer" onClick={async () => { try { + // Mark that we're logging out to prevent auto-login + sessionStorage.setItem('just_logged_out', 'true'); + const keycloakIssuer = process.env.NEXT_PUBLIC_KEYCLOAK_ISSUER; const idToken = session?.idToken; // First sign out from NextAuth (clears NextAuth cookies) await signOut({ - callbackUrl: '/signin', + callbackUrl: '/signin?logout=true', redirect: false }); @@ -377,10 +380,10 @@ export function MainNav() { `${keycloakIssuer}/protocol/openid-connect/logout` ); - // Add required parameters + // Add required parameters - include logout=true in redirect URI keycloakLogoutUrl.searchParams.append( 'post_logout_redirect_uri', - window.location.origin + '/signin' + window.location.origin + '/signin?logout=true' ); keycloakLogoutUrl.searchParams.append( 'id_token_hint', @@ -391,12 +394,12 @@ export function MainNav() { window.location.href = keycloakLogoutUrl.toString(); } else { // Fallback: just redirect to signin if we don't have Keycloak info - window.location.href = '/signin'; + window.location.href = '/signin?logout=true'; } } catch (error) { console.error('Error during logout:', error); // Fallback to simple redirect if something goes wrong - window.location.href = '/signin'; + window.location.href = '/signin?logout=true'; } }} >