diff --git a/app/signin/page.tsx b/app/signin/page.tsx
index 9a947eb9..d0564a92 100644
--- a/app/signin/page.tsx
+++ b/app/signin/page.tsx
@@ -1,15 +1,23 @@
"use client";
import { signIn, useSession } from "next-auth/react";
-import { useEffect } from "react";
+import { useEffect, useState } from "react";
+import { useSearchParams } from "next/navigation";
export default function SignIn() {
const { data: session } = useSession();
+ const searchParams = useSearchParams();
+ const signedOut = searchParams.get('signedOut') === 'true';
+ const [isRedirecting, setIsRedirecting] = useState(false);
useEffect(() => {
- // Trigger Keycloak sign-in
- signIn("keycloak", { callbackUrl: "/" });
- }, []);
+ // Only automatically sign in if not explicitly signed out
+ if (!signedOut && !isRedirecting) {
+ setIsRedirecting(true);
+ // Trigger Keycloak sign-in
+ signIn("keycloak", { callbackUrl: "/" });
+ }
+ }, [signedOut, isRedirecting]);
useEffect(() => {
if (session?.user && !session.user.nextcloudInitialized) {
@@ -38,9 +46,28 @@ export default function SignIn() {
>
-
- Redirecting to login...
-
+ {signedOut ? (
+ <>
+
+ You have been signed out
+
+
+ Click below to sign in again
+
+
+
+
+ >
+ ) : (
+
+ Redirecting to login...
+
+ )}
diff --git a/components/auth/signout-handler.tsx b/components/auth/signout-handler.tsx
index bd28645c..dd1ebce4 100644
--- a/components/auth/signout-handler.tsx
+++ b/components/auth/signout-handler.tsx
@@ -1,24 +1,58 @@
"use client";
import { useEffect } from "react";
-import { signOut } from "next-auth/react";
+import { signOut, useSession } from "next-auth/react";
import { clearAuthCookies } from "@/lib/session";
export function SignOutHandler() {
+ const { data: session } = useSession();
+
useEffect(() => {
const handleSignOut = async () => {
- // Clear only auth-related cookies
- clearAuthCookies();
-
- // Then sign out from NextAuth
- await signOut({
- callbackUrl: "/signin",
- redirect: true
- });
+ try {
+ // Clear all auth-related cookies
+ clearAuthCookies();
+
+ // First sign out from NextAuth with redirect false
+ await signOut({
+ redirect: false
+ });
+
+ // Then redirect to Keycloak logout with proper parameters
+ if (process.env.NEXT_PUBLIC_KEYCLOAK_ISSUER) {
+ const keycloakLogoutUrl = new URL(
+ `${process.env.NEXT_PUBLIC_KEYCLOAK_ISSUER}/protocol/openid-connect/logout`
+ );
+
+ // Add required parameters
+ keycloakLogoutUrl.searchParams.append(
+ 'post_logout_redirect_uri',
+ `${window.location.origin}/signin?signedOut=true`
+ );
+
+ // Add id_token_hint if available
+ if (session?.accessToken) {
+ keycloakLogoutUrl.searchParams.append(
+ 'id_token_hint',
+ session.accessToken
+ );
+ }
+
+ // Redirect to Keycloak logout
+ window.location.href = keycloakLogoutUrl.toString();
+ } else {
+ // Fallback if no Keycloak issuer is configured
+ window.location.href = '/signin?signedOut=true';
+ }
+ } catch (error) {
+ console.error('Error during logout:', error);
+ // Fallback if something goes wrong
+ window.location.href = '/signin?signedOut=true';
+ }
};
handleSignOut();
- }, []);
+ }, [session]);
return null;
}
\ No newline at end of file
diff --git a/lib/session.ts b/lib/session.ts
index 8456e090..0bc8eeaa 100644
--- a/lib/session.ts
+++ b/lib/session.ts
@@ -92,11 +92,68 @@ export async function invalidateServiceTokens(session: ExtendedSession) {
export function clearAuthCookies() {
const cookies = document.cookie.split(';');
+ console.log('Clearing all auth cookies');
+
+ // List of known auth-related cookie prefixes
+ const authCookiePrefixes = [
+ 'next-auth.',
+ '__Secure-next-auth.',
+ '__Host-next-auth.',
+ 'KEYCLOAK_',
+ 'KC_',
+ 'JSESSIONID',
+ 'OAuth_Token_Request_State',
+ 'OAUTH2_CLIENT_ID',
+ 'OAUTH2_STATE',
+ 'XSRF-TOKEN'
+ ];
+
for (const cookie of cookies) {
const [name] = cookie.split('=');
- // Only clear auth-related cookies
- if (name.trim().startsWith('next-auth.') || name.trim().startsWith('__Secure-next-auth.') || name.trim().startsWith('__Host-next-auth.')) {
- document.cookie = `${name.trim()}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
+ const trimmedName = name.trim();
+
+ // Check if this is an auth-related cookie
+ const isAuthCookie = authCookiePrefixes.some(prefix =>
+ trimmedName.startsWith(prefix)
+ );
+
+ // Also clear cookies with auth-related terms
+ const containsAuthTerm =
+ trimmedName.toLowerCase().includes('auth') ||
+ trimmedName.toLowerCase().includes('token') ||
+ trimmedName.toLowerCase().includes('session');
+
+ if (isAuthCookie || containsAuthTerm) {
+ console.log(`Clearing cookie: ${trimmedName}`);
+
+ // Clear the cookie with various domain/path combinations
+ // Standard path
+ document.cookie = `${trimmedName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
+
+ // Root domain
+ const domain = window.location.hostname.split('.').slice(-2).join('.');
+ document.cookie = `${trimmedName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=${domain};`;
+
+ // Full domain
+ document.cookie = `${trimmedName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; domain=${window.location.hostname};`;
}
}
+
+ // Clear localStorage items that might be related to authentication
+ try {
+ const authLocalStoragePrefixes = ['token', 'auth', 'session', 'keycloak', 'kc', 'user'];
+
+ for (let i = 0; i < localStorage.length; i++) {
+ const key = localStorage.key(i);
+ if (key) {
+ const keyLower = key.toLowerCase();
+ if (authLocalStoragePrefixes.some(prefix => keyLower.includes(prefix))) {
+ console.log(`Clearing localStorage: ${key}`);
+ localStorage.removeItem(key);
+ }
+ }
+ }
+ } catch (e) {
+ console.error('Error clearing localStorage:', e);
+ }
}
\ No newline at end of file