auth flow

This commit is contained in:
alma 2025-05-02 16:38:58 +02:00
parent 3be309c0fe
commit d488979109
3 changed files with 117 additions and 17 deletions

View File

@ -83,16 +83,27 @@ export const authOptions: NextAuthOptions = {
callbacks: { callbacks: {
async jwt({ token, account, profile }: any) { async jwt({ token, account, profile }: any) {
if (account && profile) { if (account && profile) {
// Just store access token and critical fields // Store access token
token.accessToken = account.access_token; token.accessToken = account.access_token;
// Make sure roles are available token.refreshToken = account.refresh_token;
if (profile.role) {
// Extract roles correctly from the raw Keycloak profile
if (profile.realm_access && profile.realm_access.roles) {
// Directly extract roles from the Keycloak profile structure
token.role = profile.realm_access.roles.map(
(role: string) => role.replace(/^ROLE_/, '').toLowerCase()
);
} else if (profile.role) {
// Fallback to using the role property if already processed
token.role = profile.role; token.role = profile.role;
token.username = profile.username || '';
token.first_name = profile.first_name || '';
token.last_name = profile.last_name || '';
} }
// Store user information
token.username = profile.preferred_username || profile.username || '';
token.first_name = profile.given_name || profile.first_name || '';
token.last_name = profile.family_name || profile.last_name || '';
} }
return token; return token;
}, },
async session({ session, token }: any) { async session({ session, token }: any) {
@ -102,7 +113,7 @@ export const authOptions: NextAuthOptions = {
session.user.id = token.sub || ""; session.user.id = token.sub || "";
// Ensure roles are passed to the session // Ensure roles are passed to the session
if (token.role) { if (token.role && Array.isArray(token.role)) {
session.user.role = token.role; session.user.role = token.role;
session.user.username = token.username || ''; session.user.username = token.username || '';
session.user.first_name = token.first_name || ''; session.user.first_name = token.first_name || '';
@ -114,6 +125,9 @@ export const authOptions: NextAuthOptions = {
session.user.first_name = ''; session.user.first_name = '';
session.user.last_name = ''; session.user.last_name = '';
} }
// Add debug log to see what roles are being passed
console.log('Session user roles:', session.user.role);
} }
return session; return session;
} }
@ -133,7 +147,7 @@ export const authOptions: NextAuthOptions = {
}, },
}, },
}, },
debug: false, debug: true, // Enable debug logs temporarily to see role information
}; };
const handler = NextAuth(authOptions); const handler = NextAuth(authOptions);

View File

@ -11,12 +11,13 @@ export default function SignIn() {
const [message, setMessage] = useState(""); const [message, setMessage] = useState("");
useEffect(() => { useEffect(() => {
// Clear cookies on errors or manual signout // Always clear cookies on signin page load to ensure fresh authentication
clearAuthCookies();
// Set error message if present
if (error) { if (error) {
console.log("Clearing auth cookies due to error:", error); console.log("Clearing auth cookies due to error:", error);
clearAuthCookies();
// Set error message
if (error === "RefreshTokenError" || error === "invalid_grant") { if (error === "RefreshTokenError" || error === "invalid_grant") {
setMessage("Your session has expired. Please sign in again."); setMessage("Your session has expired. Please sign in again.");
} else { } else {
@ -25,9 +26,24 @@ export default function SignIn() {
} }
}, [error]); }, [error]);
// Simple login function // Login function with callbackUrl to maintain original destination
const handleSignIn = () => { const handleSignIn = () => {
signIn("keycloak", { callbackUrl: "/" }); // Get the callback URL from the query parameters or use the home page
const callbackUrl = searchParams.get("callbackUrl") || "/";
// Add a timestamp parameter to avoid caching issues
const timestamp = new Date().getTime();
const authParams = {
callbackUrl,
redirect: true,
// Adding a timestamp to force Keycloak to skip any cached session
authParams: {
prompt: "login",
t: timestamp.toString()
}
};
signIn("keycloak", authParams);
}; };
return ( return (

View File

@ -114,17 +114,87 @@ export function clearAuthCookies() {
// Get all cookies to check for chunked auth cookies // Get all cookies to check for chunked auth cookies
const cookies = document.cookie.split(';'); const cookies = document.cookie.split(';');
// Clear main auth cookies // Try multiple path and domain combinations for thorough cleanup
const paths = ['/', '/auth', '/realms', '/admin'];
const domain = window.location.hostname;
const domains = [
domain,
`.${domain}`,
domain.split('.').slice(-2).join('.'),
`.${domain.split('.').slice(-2).join('.')}`
];
// Clear main auth cookies with all path/domain combinations
authCookies.forEach(cookieName => { authCookies.forEach(cookieName => {
// Basic deletion
document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; SameSite=None; Secure`; document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; SameSite=None; Secure`;
// Try more aggressive deletion with different paths and domains
paths.forEach(path => {
document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=${path}; SameSite=None; Secure`;
domains.forEach(domainValue => {
document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=${path}; domain=${domainValue}; SameSite=None; Secure`;
});
});
}); });
// Check for and clear any chunked cookies // Check for and clear any chunked cookies and cookies with dynamic names
cookies.forEach(cookie => { cookies.forEach(cookie => {
const cookieName = cookie.split('=')[0].trim(); const cookieName = cookie.split('=')[0].trim();
// Check for chunked cookies (they end with a number)
if (cookieName.startsWith('next-auth.') && /\.\d+$/.test(cookieName)) { // Clear chunked cookies (end with a number) and any auth-related cookies
if (cookieName.startsWith('next-auth.') ||
cookieName.includes('keycloak') ||
cookieName.includes('auth') ||
cookieName.includes('session') ||
/\.\d+$/.test(cookieName)) {
// Basic deletion
document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; SameSite=None; Secure`; document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; SameSite=None; Secure`;
// Try more aggressive deletion with different paths and domains
paths.forEach(path => {
document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=${path}; SameSite=None; Secure`;
domains.forEach(domainValue => {
document.cookie = `${cookieName}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=${path}; domain=${domainValue}; SameSite=None; Secure`;
});
});
} }
}); });
// Clear localStorage items related to authentication
try {
const authItems = [
'Meteor.loginToken',
'Meteor.userId',
'token',
'refreshToken',
'userId',
'userName',
'userEmail'
];
authItems.forEach(item => {
localStorage.removeItem(item);
});
// Also look for any items with auth-related names
for (let i = 0; i < localStorage.length; i++) {
const key = localStorage.key(i);
if (key && (key.includes('token') || key.includes('auth') || key.includes('session'))) {
localStorage.removeItem(key);
}
}
} catch (e) {
console.error('Failed to clear localStorage:', e);
}
// Clear all sessionStorage
try {
sessionStorage.clear();
} catch (e) {
console.error('Failed to clear sessionStorage:', e);
}
} }