auth flow

This commit is contained in:
alma 2025-05-02 10:58:27 +02:00
parent 97479aaa35
commit a171e4a713
3 changed files with 138 additions and 20 deletions

View File

@ -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() {
>
<div className="w-full max-w-md space-y-8">
<div>
<h2 className="mt-6 text-center text-3xl font-bold tracking-tight text-white">
Redirecting to login...
</h2>
{signedOut ? (
<>
<h2 className="mt-6 text-center text-3xl font-bold tracking-tight text-white">
You have been signed out
</h2>
<p className="mt-2 text-center text-lg text-white/80">
Click below to sign in again
</p>
<div className="mt-6 flex justify-center">
<button
onClick={() => signIn("keycloak", { callbackUrl: "/" })}
className="px-8 py-3 bg-white text-gray-800 rounded hover:bg-gray-100 transition-colors"
>
Sign In
</button>
</div>
</>
) : (
<h2 className="mt-6 text-center text-3xl font-bold tracking-tight text-white">
Redirecting to login...
</h2>
)}
</div>
</div>
</div>

View File

@ -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;
}

View File

@ -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);
}
}