feature/app-launcher-login-sso
This commit is contained in:
@@ -239,6 +239,20 @@ export function redirectToKeycloakLogin(systemBaseUrl: string, redirectPath: str
|
||||
throw redirect(303, buildKeycloakLoginUrl(systemBaseUrl, redirectPath));
|
||||
}
|
||||
|
||||
/**
|
||||
* URL del Hub FastAPI para llamadas server-to-server (ej. sso-exchange).
|
||||
* No aplica isDevOnlyUrl: las URLs internas de Docker son válidas aquí.
|
||||
* Lee HUB_BACKEND_URL (override explícito) → INTERNAL_HUB_URL (ya en docker-compose)
|
||||
* → fallback a URL pública del workspace (vía proxy SvelteKit del Hub).
|
||||
*/
|
||||
export function getHubBackendUrl(): string {
|
||||
const direct =
|
||||
(env.HUB_BACKEND_URL || '').trim() ||
|
||||
(env.INTERNAL_HUB_URL || '').trim();
|
||||
if (direct) return stripTrailingSlashes(direct);
|
||||
return getWorkspaceBaseUrl();
|
||||
}
|
||||
|
||||
export function buildKeycloakLogoutUrl(systemBaseUrl: string, idTokenHint?: string): string {
|
||||
const keycloakBaseUrl = getPublicKeycloakBaseUrl();
|
||||
const postLogoutRedirectUri = `${systemBaseUrl}/auth/post-logout`;
|
||||
|
||||
@@ -1,16 +1,64 @@
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
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
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user