auth flow
This commit is contained in:
parent
a171e4a713
commit
ecd587fd8c
7
app/loggedout/layout.tsx
Normal file
7
app/loggedout/layout.tsx
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export default function LoggedOutLayout({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
return children;
|
||||||
|
}
|
||||||
49
app/loggedout/page.tsx
Normal file
49
app/loggedout/page.tsx
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import { clearAuthCookies } from "@/lib/session";
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
export default function LoggedOut() {
|
||||||
|
// Clear auth cookies again on this page as an extra precaution
|
||||||
|
useEffect(() => {
|
||||||
|
// Run an additional cookie cleanup on this page
|
||||||
|
clearAuthCookies();
|
||||||
|
|
||||||
|
// Also clear session storage
|
||||||
|
try {
|
||||||
|
sessionStorage.clear();
|
||||||
|
} catch (e) {
|
||||||
|
console.error('Error clearing session storage:', e);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="min-h-screen flex items-center justify-center"
|
||||||
|
style={{
|
||||||
|
backgroundImage: "url('/signin.jpg')",
|
||||||
|
backgroundSize: 'cover',
|
||||||
|
backgroundPosition: 'center',
|
||||||
|
backgroundRepeat: 'no-repeat'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="w-full max-w-md p-8 bg-black/60 backdrop-blur-sm rounded-lg shadow-xl">
|
||||||
|
<div className="text-center">
|
||||||
|
<h2 className="text-3xl font-bold text-white mb-4">
|
||||||
|
You have been logged out
|
||||||
|
</h2>
|
||||||
|
<p className="text-white/80 mb-8">
|
||||||
|
Your session has been successfully terminated and all authentication data has been cleared.
|
||||||
|
</p>
|
||||||
|
<Link
|
||||||
|
href="/signin"
|
||||||
|
className="inline-block px-8 py-3 bg-white text-gray-800 rounded hover:bg-gray-100 transition-colors"
|
||||||
|
>
|
||||||
|
Sign In Again
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -9,15 +9,34 @@ export default function SignIn() {
|
|||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const signedOut = searchParams.get('signedOut') === 'true';
|
const signedOut = searchParams.get('signedOut') === 'true';
|
||||||
const [isRedirecting, setIsRedirecting] = useState(false);
|
const [isRedirecting, setIsRedirecting] = useState(false);
|
||||||
|
const [isFromLogout, setIsFromLogout] = useState(false);
|
||||||
|
|
||||||
|
// Check if we came from the loggedout page
|
||||||
|
useEffect(() => {
|
||||||
|
const referrer = document.referrer;
|
||||||
|
const isFromLoggedOutPage = referrer &&
|
||||||
|
(referrer.includes('/loggedout') || referrer.includes('/signout'));
|
||||||
|
|
||||||
|
if (isFromLoggedOutPage) {
|
||||||
|
console.log('Detected navigation from logout page, preventing auto-login');
|
||||||
|
setIsFromLogout(true);
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Only automatically sign in if not explicitly signed out
|
// Only automatically sign in if not explicitly signed out and not coming from logout
|
||||||
if (!signedOut && !isRedirecting) {
|
if (!signedOut && !isFromLogout && !isRedirecting && !session) {
|
||||||
setIsRedirecting(true);
|
setIsRedirecting(true);
|
||||||
|
console.log('Triggering automatic sign-in');
|
||||||
|
// Add a small delay to avoid immediate redirect which can cause loops
|
||||||
|
const timer = setTimeout(() => {
|
||||||
// Trigger Keycloak sign-in
|
// Trigger Keycloak sign-in
|
||||||
signIn("keycloak", { callbackUrl: "/" });
|
signIn("keycloak", { callbackUrl: "/" });
|
||||||
|
}, 500);
|
||||||
|
|
||||||
|
return () => clearTimeout(timer);
|
||||||
}
|
}
|
||||||
}, [signedOut, isRedirecting]);
|
}, [signedOut, isRedirecting, isFromLogout, session]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (session?.user && !session.user.nextcloudInitialized) {
|
if (session?.user && !session.user.nextcloudInitialized) {
|
||||||
@ -34,6 +53,8 @@ export default function SignIn() {
|
|||||||
}
|
}
|
||||||
}, [session]);
|
}, [session]);
|
||||||
|
|
||||||
|
const showManualLoginButton = signedOut || isFromLogout || isRedirecting;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="min-h-screen flex items-center justify-center"
|
className="min-h-screen flex items-center justify-center"
|
||||||
@ -46,17 +67,20 @@ export default function SignIn() {
|
|||||||
>
|
>
|
||||||
<div className="w-full max-w-md space-y-8">
|
<div className="w-full max-w-md space-y-8">
|
||||||
<div>
|
<div>
|
||||||
{signedOut ? (
|
{showManualLoginButton ? (
|
||||||
<>
|
<>
|
||||||
<h2 className="mt-6 text-center text-3xl font-bold tracking-tight text-white">
|
<h2 className="mt-6 text-center text-3xl font-bold tracking-tight text-white">
|
||||||
You have been signed out
|
{signedOut || isFromLogout ? 'You have been signed out' : 'Welcome Back'}
|
||||||
</h2>
|
</h2>
|
||||||
<p className="mt-2 text-center text-lg text-white/80">
|
<p className="mt-2 text-center text-lg text-white/80">
|
||||||
Click below to sign in again
|
Click below to sign in
|
||||||
</p>
|
</p>
|
||||||
<div className="mt-6 flex justify-center">
|
<div className="mt-6 flex justify-center">
|
||||||
<button
|
<button
|
||||||
onClick={() => signIn("keycloak", { callbackUrl: "/" })}
|
onClick={() => {
|
||||||
|
setIsRedirecting(true);
|
||||||
|
signIn("keycloak", { callbackUrl: "/" });
|
||||||
|
}}
|
||||||
className="px-8 py-3 bg-white text-gray-800 rounded hover:bg-gray-100 transition-colors"
|
className="px-8 py-3 bg-white text-gray-800 rounded hover:bg-gray-100 transition-colors"
|
||||||
>
|
>
|
||||||
Sign In
|
Sign In
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { signOut, useSession } from "next-auth/react";
|
import { useSession } from "next-auth/react";
|
||||||
import { clearAuthCookies } from "@/lib/session";
|
import { clearAuthCookies } from "@/lib/session";
|
||||||
|
|
||||||
export function SignOutHandler() {
|
export function SignOutHandler() {
|
||||||
@ -10,49 +10,63 @@ export function SignOutHandler() {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleSignOut = async () => {
|
const handleSignOut = async () => {
|
||||||
try {
|
try {
|
||||||
// Clear all auth-related cookies
|
// First, clear all auth-related cookies to ensure we break any local sessions
|
||||||
clearAuthCookies();
|
clearAuthCookies();
|
||||||
|
|
||||||
// First sign out from NextAuth with redirect false
|
// Create a temporary HTML form for direct POST logout (more reliable than redirect)
|
||||||
await signOut({
|
if (process.env.NEXT_PUBLIC_KEYCLOAK_ISSUER && session?.accessToken) {
|
||||||
redirect: false
|
console.log('Directly calling Keycloak logout endpoint');
|
||||||
});
|
|
||||||
|
|
||||||
// Then redirect to Keycloak logout with proper parameters
|
// Create a hidden form for POST logout
|
||||||
if (process.env.NEXT_PUBLIC_KEYCLOAK_ISSUER) {
|
const form = document.createElement('form');
|
||||||
const keycloakLogoutUrl = new URL(
|
form.method = 'POST';
|
||||||
`${process.env.NEXT_PUBLIC_KEYCLOAK_ISSUER}/protocol/openid-connect/logout`
|
form.action = `${process.env.NEXT_PUBLIC_KEYCLOAK_ISSUER}/protocol/openid-connect/logout`;
|
||||||
);
|
|
||||||
|
|
||||||
// Add required parameters
|
// Add the id_token_hint
|
||||||
keycloakLogoutUrl.searchParams.append(
|
if (session.accessToken) {
|
||||||
'post_logout_redirect_uri',
|
const tokenInput = document.createElement('input');
|
||||||
`${window.location.origin}/signin?signedOut=true`
|
tokenInput.type = 'hidden';
|
||||||
);
|
tokenInput.name = 'id_token_hint';
|
||||||
|
tokenInput.value = session.accessToken;
|
||||||
// Add id_token_hint if available
|
form.appendChild(tokenInput);
|
||||||
if (session?.accessToken) {
|
|
||||||
keycloakLogoutUrl.searchParams.append(
|
|
||||||
'id_token_hint',
|
|
||||||
session.accessToken
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Redirect to Keycloak logout
|
// Add post_logout_redirect_uri pointing to a special loggedout page
|
||||||
window.location.href = keycloakLogoutUrl.toString();
|
const redirectInput = document.createElement('input');
|
||||||
|
redirectInput.type = 'hidden';
|
||||||
|
redirectInput.name = 'post_logout_redirect_uri';
|
||||||
|
redirectInput.value = `${window.location.origin}/loggedout`;
|
||||||
|
form.appendChild(redirectInput);
|
||||||
|
|
||||||
|
// Append to body and submit
|
||||||
|
document.body.appendChild(form);
|
||||||
|
form.submit();
|
||||||
} else {
|
} else {
|
||||||
// Fallback if no Keycloak issuer is configured
|
console.log('No Keycloak configuration found, performing simple redirect');
|
||||||
window.location.href = '/signin?signedOut=true';
|
// Fallback if no Keycloak config or session
|
||||||
|
window.location.href = '/loggedout';
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error during logout:', error);
|
console.error('Error during logout:', error);
|
||||||
// Fallback if something goes wrong
|
// Fallback if something goes wrong
|
||||||
window.location.href = '/signin?signedOut=true';
|
window.location.href = '/loggedout';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Add a slight delay to ensure useSession has loaded
|
||||||
|
const timer = setTimeout(() => {
|
||||||
handleSignOut();
|
handleSignOut();
|
||||||
|
}, 100);
|
||||||
|
|
||||||
|
return () => clearTimeout(timer);
|
||||||
}, [session]);
|
}, [session]);
|
||||||
|
|
||||||
return null;
|
return (
|
||||||
|
<div className="w-full h-full flex items-center justify-center">
|
||||||
|
<div className="text-center">
|
||||||
|
<h2 className="text-2xl font-bold">Logging out...</h2>
|
||||||
|
<p className="text-gray-500 mt-2">Please wait while we sign you out.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user