FEAT: Migrate authentication system from Directus to Keycloak, implementing token refresh and enhancing session management
This commit is contained in:
@@ -1,80 +1,101 @@
|
||||
/**
|
||||
* Check if the request is authenticated via either:
|
||||
* 1. x-tag header (for webhooks/external calls)
|
||||
* 2. Directus token (for Directus authenticated users)
|
||||
* 3. OIDC session (for Keycloak authenticated users)
|
||||
* Check if the request is authenticated via Keycloak OIDC session
|
||||
*/
|
||||
export const isAuthenticated = async (event: any): Promise<boolean> => {
|
||||
// Check x-tag header authentication (existing method)
|
||||
const xTagHeader = getRequestHeader(event, "x-tag");
|
||||
if (xTagHeader && (xTagHeader === "094ut234" || xTagHeader === "pjnvü1230")) {
|
||||
console.log('[auth] Authenticated via x-tag header');
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check Directus token authentication
|
||||
try {
|
||||
const directusToken = getCookie(event, 'directus_token');
|
||||
console.log('[auth] Checking Directus token:', directusToken ? 'present' : 'not found');
|
||||
|
||||
if (directusToken) {
|
||||
// Validate Directus token is not expired
|
||||
const directusExpiry = getCookie(event, 'directus_token_expired_at');
|
||||
console.log('[auth] Directus expiry cookie:', directusExpiry ? directusExpiry : 'not found');
|
||||
|
||||
if (directusExpiry) {
|
||||
const expiryTime = parseInt(directusExpiry);
|
||||
const currentTime = Date.now();
|
||||
console.log('[auth] Directus expiry check:', { currentTime, expiryTime, isValid: currentTime < expiryTime });
|
||||
|
||||
if (currentTime < expiryTime) {
|
||||
console.log('[auth] Authenticated via Directus token');
|
||||
return true;
|
||||
} else {
|
||||
console.log('[auth] Directus token expired');
|
||||
}
|
||||
} else {
|
||||
// If no expiry cookie, assume token is valid
|
||||
console.log('[auth] Authenticated via Directus token (no expiry check)');
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('[auth] Directus token check failed:', error);
|
||||
}
|
||||
console.log('[auth] Checking authentication for:', event.node.req.url);
|
||||
|
||||
// Check OIDC session authentication
|
||||
try {
|
||||
const oidcSession = getCookie(event, 'nuxt-oidc-auth');
|
||||
console.log('[auth] Checking OIDC session:', oidcSession ? 'present' : 'not found');
|
||||
console.log('[auth] OIDC session cookie:', oidcSession ? 'present' : 'not found');
|
||||
|
||||
if (oidcSession) {
|
||||
// Note: OIDC session might be encrypted, we'll validate it properly in session endpoint
|
||||
console.log('[auth] OIDC session found, type:', oidcSession.startsWith('Fe26.2**') ? 'encrypted' : 'plain');
|
||||
console.log('[auth] Authenticated via OIDC session');
|
||||
return true;
|
||||
if (!oidcSession) {
|
||||
console.log('[auth] No OIDC session found');
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log('[auth] OIDC session check failed:', error);
|
||||
}
|
||||
|
||||
console.log('[auth] No valid authentication found');
|
||||
return false;
|
||||
// Parse and validate session data
|
||||
let sessionData;
|
||||
try {
|
||||
sessionData = JSON.parse(oidcSession);
|
||||
} catch (parseError) {
|
||||
console.error('[auth] Failed to parse session cookie:', parseError);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Validate session structure
|
||||
if (!sessionData.user || !sessionData.accessToken) {
|
||||
console.error('[auth] Invalid session structure:', {
|
||||
hasUser: !!sessionData.user,
|
||||
hasAccessToken: !!sessionData.accessToken
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if session is still valid
|
||||
if (sessionData.expiresAt && Date.now() > sessionData.expiresAt) {
|
||||
console.log('[auth] Session expired:', {
|
||||
expiresAt: sessionData.expiresAt,
|
||||
currentTime: Date.now(),
|
||||
expiredSince: Date.now() - sessionData.expiresAt
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log('[auth] Valid OIDC session found for user:', {
|
||||
id: sessionData.user.id,
|
||||
email: sessionData.user.email
|
||||
});
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
console.error('[auth] OIDC session check failed:', error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export const requireAuth = async (event: any) => {
|
||||
const authenticated = await isAuthenticated(event);
|
||||
if (!authenticated) {
|
||||
console.log('[requireAuth] Authentication failed for:', event.node.req.url);
|
||||
console.log('[requireAuth] Available headers:', Object.keys(event.node.req.headers));
|
||||
console.log('[requireAuth] Available cookies:', Object.keys(event.node.req.headers.cookie ? parseCookies(event.node.req.headers.cookie) : {}));
|
||||
throw createError({
|
||||
statusCode: 401,
|
||||
statusMessage: "Authentication required. Please provide x-tag header or valid session."
|
||||
statusMessage: "Authentication required. Please login with Keycloak."
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the authenticated user from the session
|
||||
*/
|
||||
export const getAuthenticatedUser = async (event: any): Promise<any | null> => {
|
||||
try {
|
||||
const oidcSession = getCookie(event, 'nuxt-oidc-auth');
|
||||
|
||||
if (!oidcSession) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const sessionData = JSON.parse(oidcSession);
|
||||
|
||||
// Validate session
|
||||
if (!sessionData.user || !sessionData.accessToken) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check if session is still valid
|
||||
if (sessionData.expiresAt && Date.now() > sessionData.expiresAt) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return sessionData.user;
|
||||
} catch (error) {
|
||||
console.error('[getAuthenticatedUser] Error:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function parseCookies(cookieString: string): Record<string, string> {
|
||||
return cookieString.split(';').reduce((cookies: Record<string, string>, cookie) => {
|
||||
const [name, value] = cookie.trim().split('=');
|
||||
|
||||
Reference in New Issue
Block a user