98 lines
3.4 KiB
TypeScript
98 lines
3.4 KiB
TypeScript
import { redirect, isRedirect } from '@sveltejs/kit';
|
|
import type { PageServerLoad } from './$types';
|
|
import { clearAuthTokens } from '$lib/server/api';
|
|
import { setAccessTokenCookies } from '$lib/server/access-token-cookie';
|
|
import {
|
|
getWorkspaceLoginUrl,
|
|
readWorkspaceReturnPath,
|
|
clearWorkspaceReturnPath,
|
|
storeReturnPath,
|
|
redirectToKeycloakAuthorization,
|
|
redirectToKeycloakLogin,
|
|
getHubBackendUrl,
|
|
isSecureContext,
|
|
} from '$lib/server/workspace-auth';
|
|
|
|
export const load: PageServerLoad = async ({ cookies, url }) => {
|
|
console.error('[LOGIN] url.pathname:', url.pathname, '| params:', Object.fromEntries(url.searchParams));
|
|
|
|
// Relay SSO: el Hub App Launcher redirige aquí con ?relay=UUID4.
|
|
// Se canjea server-to-server (endpoint público, sin Bearer) por access_token + refresh_token.
|
|
const relayToken = url.searchParams.get('relay');
|
|
if (relayToken) {
|
|
try {
|
|
const hubBackendUrl = getHubBackendUrl();
|
|
const exchangeRes = await fetch(`${hubBackendUrl}/api/v1/auth/sso-exchange`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ relay_token: relayToken })
|
|
});
|
|
|
|
if (exchangeRes.ok) {
|
|
const data = await exchangeRes.json();
|
|
const isProduction = isSecureContext();
|
|
|
|
setAccessTokenCookies(cookies, data.access_token, {
|
|
secure: isProduction,
|
|
maxAge: 60 * 60 * 24 * 7
|
|
});
|
|
if (data.refresh_token) {
|
|
cookies.set('refresh_token', data.refresh_token, {
|
|
path: '/',
|
|
httpOnly: true,
|
|
secure: isProduction,
|
|
sameSite: 'lax',
|
|
maxAge: 60 * 60 * 24 * 30
|
|
});
|
|
}
|
|
|
|
const redirectTo =
|
|
url.searchParams.get('redirect') ||
|
|
readWorkspaceReturnPath(cookies, '/dashboard');
|
|
clearWorkspaceReturnPath(cookies);
|
|
throw redirect(303, redirectTo);
|
|
}
|
|
// relay inválido/expirado (410) o Hub no disponible → continúa al flujo normal
|
|
} catch (err) {
|
|
if (isRedirect(err)) throw err;
|
|
// silencio: cae al flujo normal de login
|
|
}
|
|
}
|
|
|
|
clearAuthTokens(cookies);
|
|
|
|
// Workspace redirige de vuelta aquí con ?sso_verified=1 después de que el usuario
|
|
// se autenticó en Workspace (que usa el mismo Keycloak central).
|
|
// En ese momento la sesión KC ya existe en el browser → prompt=none funciona sin
|
|
// mostrar ninguna pantalla de login.
|
|
if (url.searchParams.get('sso_verified') === '1') {
|
|
const existingReturnPath = readWorkspaceReturnPath(cookies, '');
|
|
const intendedPath =
|
|
existingReturnPath && existingReturnPath !== '/login'
|
|
? existingReturnPath
|
|
: (url.searchParams.get('redirect') || '/dashboard');
|
|
|
|
storeReturnPath(cookies, intendedPath);
|
|
redirectToKeycloakAuthorization(url.origin, intendedPath);
|
|
}
|
|
|
|
// El Hub devolvió ?redirect= sin sso_verified=1.
|
|
// Ir directo a Keycloak SIN prompt=none para romper el loop Hub↔login:
|
|
// - KC tiene sesión activa → devuelve código al callback → éxito.
|
|
// - KC no tiene sesión → muestra el form de login → callback → éxito.
|
|
const redirectParam = url.searchParams.get('redirect');
|
|
if (redirectParam) {
|
|
const existingReturnPath = readWorkspaceReturnPath(cookies, '');
|
|
const intendedPath =
|
|
existingReturnPath && existingReturnPath !== '/login'
|
|
? existingReturnPath
|
|
: redirectParam;
|
|
storeReturnPath(cookies, intendedPath);
|
|
redirectToKeycloakLogin(url.origin, intendedPath);
|
|
}
|
|
|
|
// Primera visita sin ningún parámetro → guardar destino y mandar al workspace.
|
|
storeReturnPath(cookies, '/dashboard');
|
|
throw redirect(303, getWorkspaceLoginUrl(url.origin));
|
|
};
|