auth flow
This commit is contained in:
parent
97479aaa35
commit
a171e4a713
@ -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>
|
||||
|
||||
@ -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;
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user