perbaikan keycloak dan config

This commit is contained in:
Fanrouver
2026-01-06 15:34:11 +07:00
parent bb12c9a0e9
commit 21f6b63ce4
10 changed files with 259 additions and 80 deletions
+56 -12
View File
@@ -137,39 +137,77 @@ export default defineEventHandler(async (event) => {
return sendRedirect(event, `/LoginPage?error=${errorMsg}`);
}
// Store minimal session data in cookie to reduce size
// The ID token contains user info, so we can decode it when needed
const sessionData = {
// Store only essential user info (can be decoded from ID token if needed)
user: {
id: idTokenPayload.sub,
email: idTokenPayload.email,
name: idTokenPayload.name || idTokenPayload.preferred_username,
preferred_username: idTokenPayload.preferred_username,
given_name: idTokenPayload.given_name,
family_name: idTokenPayload.family_name,
},
// Store tokens - these are necessary for API calls
// Note: These JWT tokens are large, but necessary for authentication
accessToken: tokens.access_token,
idToken: tokens.id_token,
refreshToken: tokens.refresh_token,
// CHANGED: Use custom session duration instead of Keycloak's token expiry
// Session metadata
expiresAt: Date.now() + (SESSION_DURATION * 1000),
createdAt: Date.now(),
};
const isSecure = process.env.NODE_ENV === 'production' ||
// Determine if we should use secure cookies
// For localhost, always use secure: false
const isSecure = process.env.NODE_ENV === 'production' &&
event.node.req.headers['x-forwarded-proto'] === 'https';
console.log('🔗 Setting session cookie with secure flag:', isSecure);
console.log('⏱️ Session duration:', SESSION_DURATION, 'seconds');
console.log('🌐 Request host:', event.node.req.headers.host);
console.log('🔒 Protocol:', event.node.req.headers['x-forwarded-proto'] || 'http');
setCookie(event, 'user_session', JSON.stringify(sessionData), {
// Set cookie with proper settings for localhost
// For localhost HTTP, we need secure: false and sameSite: 'lax'
// IMPORTANT: Ensure domain is not set for localhost (allows cookie to work)
const cookieOptions: any = {
httpOnly: true,
secure: isSecure,
sameSite: 'lax',
// CHANGED: Use custom session duration (7 days default)
sameSite: 'lax' as const,
maxAge: SESSION_DURATION,
path: '/',
});
// Explicitly don't set domain for localhost - this is important!
// Setting domain to 'localhost' can cause cookies to not work
};
// For localhost, don't set domain (allows cookie to work on localhost)
// Only set domain in production if needed
if (process.env.NODE_ENV === 'production' && !event.node.req.headers.host?.includes('localhost')) {
// Optionally set domain in production
// cookieOptions.domain = '.yourdomain.com';
}
// Store session in server-side store and use session ID in cookie
// This avoids cookie size limits (4KB)
const { createSession } = await import('~/server/utils/sessionStore');
const sessionId = createSession(sessionData);
console.log('💾 Session stored server-side with ID:', sessionId.substring(0, 8) + '...');
console.log('📦 Session ID cookie size: ~64 bytes (much smaller!)');
// Store only the session ID in the cookie (much smaller)
setCookie(event, 'user_session', sessionId, cookieOptions);
console.log('✅ Session ID cookie set in response headers (will be available in next request)');
console.log('✅ Session cookie created successfully');
console.log('🍪 Cookie details:');
console.log(' - Path: /');
console.log(' - Secure:', isSecure);
console.log(' - SameSite: lax');
console.log(' - HttpOnly: true');
console.log(' - MaxAge:', SESSION_DURATION, 'seconds');
console.log(' - Host:', event.node.req.headers.host);
// Auto-sync user data to database (first time login check)
// Pass session createdAt as loginTime to sync function
@@ -182,11 +220,17 @@ export default defineEventHandler(async (event) => {
console.error('⚠️ Failed to auto-sync user on login:', syncError);
}
const testCookie = getCookie(event, 'user_session');
console.log('🧪 Cookie test - can read back in this handler (Expected False):', !!testCookie);
// IMPORTANT: Ensure cookie is set before redirect
// The cookie should be in the Set-Cookie header of the redirect response
console.log('↪️ Redirecting to dashboard with cookie in response headers...');
console.log('↪️ Redirecting to dashboard...');
return sendRedirect(event, '/dashboard?authenticated=true');
// Note: In H3/Nitro, setCookie automatically adds Set-Cookie header to response
// The cookie will be available in the browser after the redirect
// We can't verify it in the same request, but it should be set correctly
// Use sendRedirect - it should include the Set-Cookie header
// The browser will receive the cookie and include it in the next request
return sendRedirect(event, '/dashboard?authenticated=true', 302);
} catch (error: any) {
console.error('❌ === CALLBACK ERROR ===');
+11 -6
View File
@@ -5,16 +5,21 @@ export default defineEventHandler(async (event) => {
console.log('🚪 Logout handler called');
// Get the current session to retrieve tokens
const sessionCookie = getCookie(event, 'user_session');
const sessionId = getCookie(event, 'user_session');
let idToken = null;
if (sessionCookie) {
if (sessionId) {
try {
const session = JSON.parse(sessionCookie);
idToken = session.idToken;
console.log('🔑 ID token found in session:', !!idToken);
const { getSession, deleteSession } = await import('~/server/utils/sessionStore');
const session = getSession(sessionId);
if (session) {
idToken = session.idToken;
console.log('🔑 ID token found in session:', !!idToken);
// Delete session from store
deleteSession(sessionId);
}
} catch (error) {
console.warn('⚠️ Could not parse session cookie:', error);
console.warn('⚠️ Could not retrieve session:', error);
}
} else {
console.warn('⚠️ No session cookie found');
+30 -15
View File
@@ -1,4 +1,5 @@
// server/api/auth/session.get.ts
import type { SessionResponse } from '~/types/auth'
// Helper function to safely decode the JWT payload (Access Token or ID Token)
const decodeTokenPayload = (token: string | undefined): any | null => {
@@ -23,10 +24,10 @@ const decodeTokenPayload = (token: string | undefined): any | null => {
export default defineEventHandler(async (event) => {
console.log("🔍 Session endpoint called");
const sessionCookie = getCookie(event, "user_session");
console.log("🍪 Session cookie exists:", !!sessionCookie);
const sessionId = getCookie(event, "user_session");
console.log("🍪 Session cookie exists:", !!sessionId);
if (!sessionCookie) {
if (!sessionId) {
console.log("❌ No session cookie found");
throw createError({
statusCode: 401,
@@ -35,8 +36,20 @@ export default defineEventHandler(async (event) => {
}
try {
const session = JSON.parse(sessionCookie);
console.log("📋 Session parsed successfully");
// Get session from server-side store using session ID
const { getSession } = await import('~/server/utils/sessionStore');
const session = getSession(sessionId);
if (!session) {
console.log("❌ Session not found or expired");
deleteCookie(event, "user_session");
throw createError({
statusCode: 401,
statusMessage: "Session expired or invalid",
});
}
console.log("📋 Session retrieved from store successfully");
const isExpired = Date.now() > session.expiresAt;
console.log("   Is Expired:", isExpired);
@@ -55,27 +68,29 @@ export default defineEventHandler(async (event) => {
const idTokenPayload = decodeTokenPayload(session.idToken);
const accessTokenPayload = decodeTokenPayload(session.accessToken);
// Final response object for the frontend debug page
const sessionResponse = {
// Final response object - ensure it matches SessionResponse interface
const sessionResponse: SessionResponse & {
idTokenPayload?: any
accessTokenPayload?: any
fullSessionObject?: any
status?: string
} = {
success: true,
// Basic User Info
user: session.user,
// Raw Tokens
idToken: session.idToken,
// Raw Tokens (optional in SessionResponse)
accessToken: session.accessToken,
refreshToken: session.refreshToken,
// Session Timestamps
// Session Timestamps (optional in SessionResponse)
expiresAt: session.expiresAt,
createdAt: session.createdAt,
// Parsed Payloads
// Additional debug fields (not in SessionResponse interface)
idToken: session.idToken,
idTokenPayload: idTokenPayload,
accessTokenPayload: accessTokenPayload,
// Raw Session Data (for Debug section)
fullSessionObject: session,
status: "authenticated",
};
+4 -13
View File
@@ -5,26 +5,17 @@
export default defineEventHandler(async (event) => {
console.log("🔄 User sync endpoint called");
const sessionCookie = getCookie(event, "user_session");
const { getSessionFromCookie } = await import('~/server/utils/sessionStore');
const session = await getSessionFromCookie(event);
if (!sessionCookie) {
if (!session) {
throw createError({
statusCode: 401,
statusMessage: "No session cookie found",
statusMessage: "No session found or session expired",
});
}
try {
const session = JSON.parse(sessionCookie);
const isExpired = Date.now() > session.expiresAt;
if (isExpired) {
deleteCookie(event, "user_session");
throw createError({
statusCode: 401,
statusMessage: "Session expired",
});
}
// Use the shared sync utility
// Use session createdAt as loginTime, or current time if not available