diff --git a/frontend/src/lib/server/api.ts b/frontend/src/lib/server/api.ts index 3cd200c9..6d8c9980 100644 --- a/frontend/src/lib/server/api.ts +++ b/frontend/src/lib/server/api.ts @@ -9,6 +9,7 @@ import { getAccessTokenFromCookies, setAccessTokenCookies } from '$lib/server/access-token-cookie'; +import { isSecureContext } from '$lib/server/workspace-auth'; /** * Obtiene y normaliza la URL base de la API para llamadas desde el servidor @@ -57,7 +58,7 @@ export function setAuthTokens( refreshToken?: string ) { setAccessTokenCookies(cookies, accessToken, { - secure: process.env.NODE_ENV === 'production', + secure: isSecureContext(), maxAge: 60 * 60 * 24 * 7 // 7 días }); @@ -66,7 +67,7 @@ export function setAuthTokens( path: '/', httpOnly: true, // *** HttpOnly: JS nunca lee el refresh_token *** sameSite: 'lax', - secure: process.env.NODE_ENV === 'production', + secure: isSecureContext(), maxAge: 60 * 60 * 24 * 30 // 30 días }); } diff --git a/frontend/src/lib/server/workspace-auth.ts b/frontend/src/lib/server/workspace-auth.ts index c4f6593f..d1a4f628 100644 --- a/frontend/src/lib/server/workspace-auth.ts +++ b/frontend/src/lib/server/workspace-auth.ts @@ -4,6 +4,17 @@ import { redirect, type Cookies } from '@sveltejs/kit'; const DEFAULT_WORKSPACE_BASE_URL = 'https://workspace.aduanasoft.com'; const RETURN_PATH_COOKIE = 'workspace_return_path'; +/** + * Returns true only when the public-facing URL uses HTTPS. + * Use this for cookie `secure` flag instead of NODE_ENV so that + * cookies work on HTTP LAN dev environments (e.g. 192.168.x.x). + */ +export function isSecureContext(): boolean { + const origin = (env.ORIGIN || process.env.ORIGIN || '').trim(); + if (origin) return origin.startsWith('https://'); + return process.env.NODE_ENV === 'production'; +} + function stripTrailingSlashes(value: string): string { return value.replace(/\/+$/, ''); } @@ -51,9 +62,9 @@ export function getWorkspaceLoginUrl( if (options?.forPostLogout) { return `${workspaceBaseUrl}/login`; } - // return_to points to /login so that after Workspace auth the browser lands on - // /login, which immediately attempts a prompt=none KC auth. - const loginUrl = `${systemBaseUrl}/login`; + // return_to includes sso_verified=1 so the workspace preserves it when redirecting + // back, regardless of what additional params the workspace appends. + const loginUrl = `${systemBaseUrl}/login?sso_verified=1`; return `${workspaceBaseUrl}/login?return_to=${encodeURIComponent(loginUrl)}`; } @@ -63,7 +74,7 @@ export function storeReturnPath(cookies: Cookies, path: string): void { path: '/', httpOnly: true, sameSite: 'lax', - secure: env.NODE_ENV === 'production', + secure: isSecureContext(), maxAge: 60 * 10 }); } @@ -100,7 +111,7 @@ export function storeWorkspaceReturnPath(cookies: Cookies, url: URL): string { path: '/', httpOnly: true, sameSite: 'lax', - secure: env.NODE_ENV === 'production', + secure: isSecureContext(), maxAge: 60 * 10 }); diff --git a/frontend/src/routes/api-sveltekit/auth/switch-tenant/+server.ts b/frontend/src/routes/api-sveltekit/auth/switch-tenant/+server.ts index cd2781f7..143fe048 100644 --- a/frontend/src/routes/api-sveltekit/auth/switch-tenant/+server.ts +++ b/frontend/src/routes/api-sveltekit/auth/switch-tenant/+server.ts @@ -40,7 +40,8 @@ export const POST = async ({ request, cookies, fetch }: RequestEvent) => { if (!hasAccess) { return json({ error: 'Access denied to tenant' }, { status: 403 }); } - const isProduction = process.env.NODE_ENV === 'production'; + const { isSecureContext } = await import('$lib/server/workspace-auth'); + const isProduction = isSecureContext(); cookies.set('sso_tenant_id', String(tenant_id), { path: '/', httpOnly: true, diff --git a/frontend/src/routes/auth/callback/+page.server.ts b/frontend/src/routes/auth/callback/+page.server.ts index d6d3e80b..3a7c6cc1 100644 --- a/frontend/src/routes/auth/callback/+page.server.ts +++ b/frontend/src/routes/auth/callback/+page.server.ts @@ -77,7 +77,8 @@ export const load: PageServerLoad = async ({ url, cookies, fetch }) => { // Establecer las cookies en el servidor // access_token → NO HttpOnly (el cliente JS lo usa para el header Authorization) // refresh_token → HttpOnly (el JS nunca lo lee; el servidor lo gestiona) - const isProduction = process.env.NODE_ENV === 'production'; + const { isSecureContext } = await import('$lib/server/workspace-auth'); + const isProduction = isSecureContext(); setAccessTokenCookies(cookies, tokens.access_token, { secure: isProduction, diff --git a/frontend/src/routes/auth/post-logout/+server.ts b/frontend/src/routes/auth/post-logout/+server.ts index e7bda139..97807e33 100644 --- a/frontend/src/routes/auth/post-logout/+server.ts +++ b/frontend/src/routes/auth/post-logout/+server.ts @@ -7,7 +7,7 @@ import { getWorkspaceLoginUrl } from '$lib/server/workspace-auth'; * This URL is covered by the app's registered wildcard in KC (e.g. anexo76-dev.aduanasoft.com/*). * We then send the user to workspace login so it can apply myApps() launcher logic. */ -export const GET: RequestHandler = async ({ request }) => { - const systemBaseUrl = new URL(request.url).origin; +export const GET: RequestHandler = async ({ request, url }) => { + const systemBaseUrl = url.origin; throw redirect(303, getWorkspaceLoginUrl(systemBaseUrl, { forPostLogout: true })); }; diff --git a/frontend/src/routes/auth/sso/+page.server.ts b/frontend/src/routes/auth/sso/+page.server.ts index 9cced10e..1a87a7d6 100644 --- a/frontend/src/routes/auth/sso/+page.server.ts +++ b/frontend/src/routes/auth/sso/+page.server.ts @@ -115,8 +115,9 @@ export const load: PageServerLoad = async ({ url, cookies }) => { } } - const isProduction = process.env.NODE_ENV === 'production'; - console.log('[SSO] NODE_ENV:', process.env.NODE_ENV, '→ isProduction:', isProduction); + const { isSecureContext } = await import('$lib/server/workspace-auth'); + const isProduction = isSecureContext(); + console.log('[SSO] ORIGIN-based secure context:', isProduction); // access_token — NO HttpOnly (Bearer desde JS); fragmentado si el JWT supera ~4KB setAccessTokenCookies(cookies, tokens.access_token, { diff --git a/frontend/src/routes/logout/+server.ts b/frontend/src/routes/logout/+server.ts index 0b0f6474..162e4a65 100644 --- a/frontend/src/routes/logout/+server.ts +++ b/frontend/src/routes/logout/+server.ts @@ -3,8 +3,8 @@ import type { RequestHandler } from './$types'; import { clearAccessTokenCookies } from '$lib/server/access-token-cookie'; import { buildKeycloakLogoutUrl, clearWorkspaceReturnPath } from '$lib/server/workspace-auth'; -export const POST: RequestHandler = async ({ cookies, request }) => { - const systemBaseUrl = new URL(request.url).origin; +export const POST: RequestHandler = async ({ cookies, request, url }) => { + const systemBaseUrl = url.origin; // Eliminar todas las cookies de autenticación (access_token puede estar fragmentado) clearAccessTokenCookies(cookies);