Files
plantillas-proyectos/frontend/src/lib/server/workspace-auth.ts
Galindo97 52577b9e66 refactor: update SSO handling and remove deprecated components
- Changed the hub-net network configuration to external in docker-compose.
- Removed the Single Sign-On (SSO) service implementation and associated login form component.
- Enhanced authentication callback logic to improve error handling and redirect management.
- Updated various routes to streamline login and authentication processes, ensuring proper redirection to workspace login.
- Cleaned up unused code and improved overall structure for better maintainability.
2026-05-11 17:17:37 -05:00

157 lines
4.8 KiB
TypeScript

import { env } from '$env/dynamic/private';
import { redirect, type Cookies } from '@sveltejs/kit';
const DEFAULT_WORKSPACE_BASE_URL = 'https://workspace.aduanasoft.com';
const RETURN_PATH_COOKIE = 'workspace_return_path';
function stripTrailingSlashes(value: string): string {
return value.replace(/\/+$/, '');
}
function isInternalOnlyHost(rawUrl: string): boolean {
try {
const parsed = new URL(rawUrl);
const host = parsed.hostname.toLowerCase();
return host === 'host.docker.internal' || host === 'backend' || host === 'hub-keycloak';
} catch {
return false;
}
}
export function getWorkspaceBaseUrl(): string {
const candidates = [
(env.VITE_HUB_URL || '').trim(),
(env.HUB_URL || '').trim(),
DEFAULT_WORKSPACE_BASE_URL
].filter(Boolean);
for (const candidate of candidates) {
if (!isInternalOnlyHost(candidate)) {
return stripTrailingSlashes(candidate);
}
}
return DEFAULT_WORKSPACE_BASE_URL;
}
export type WorkspaceLoginUrlOptions = {
/**
* URL del login del Hub sin `return_to`. Usar en `post_logout_redirect_uri` para que,
* tras logout en KC, el Hub aplique myApps() (launcher si el usuario tiene varias apps).
* Con `return_to` a Anexo76, el re-login siempre rebotaba a esa app aunque hubiera más.
*/
forPostLogout?: boolean;
};
export function getWorkspaceLoginUrl(
systemBaseUrl: string,
options?: WorkspaceLoginUrlOptions
): string {
const workspaceBaseUrl = getWorkspaceBaseUrl();
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 `${workspaceBaseUrl}/login?return_to=${encodeURIComponent(loginUrl)}`;
}
export function storeReturnPath(cookies: Cookies, path: string): void {
if (!path || !path.startsWith('/')) return;
cookies.set(RETURN_PATH_COOKIE, path, {
path: '/',
httpOnly: true,
sameSite: 'lax',
secure: env.NODE_ENV === 'production',
maxAge: 60 * 10
});
}
export function getPublicKeycloakBaseUrl(): string {
const configuredKeycloakUrl = (env.VITE_KEYCLOAK_URL || '').trim();
if (configuredKeycloakUrl) {
return stripTrailingSlashes(configuredKeycloakUrl);
}
return `${getWorkspaceBaseUrl()}/kcauth`;
}
export function getKeycloakRealm(): string {
return (env.KEYCLOAK_REALM || env.VITE_KEYCLOAK_REALM || 'master').trim();
}
export function getKeycloakClientId(): string {
return (env.KEYCLOAK_CLIENT_ID || env.VITE_KEYCLOAK_CLIENT_ID || 'anexo76-frontend').trim();
}
export function getCleanReturnPath(url: URL): string {
const cleanParams = new URLSearchParams(url.searchParams);
cleanParams.delete('sso_verified');
const queryString = cleanParams.toString();
return queryString ? `${url.pathname}?${queryString}` : url.pathname;
}
export function storeWorkspaceReturnPath(cookies: Cookies, url: URL): string {
const returnPath = getCleanReturnPath(url);
cookies.set(RETURN_PATH_COOKIE, returnPath, {
path: '/',
httpOnly: true,
sameSite: 'lax',
secure: env.NODE_ENV === 'production',
maxAge: 60 * 10
});
return returnPath;
}
export function readWorkspaceReturnPath(cookies: Cookies, fallbackPath: string): string {
const storedReturnPath = cookies.get(RETURN_PATH_COOKIE);
if (storedReturnPath && storedReturnPath.startsWith('/')) {
return storedReturnPath;
}
return fallbackPath;
}
export function clearWorkspaceReturnPath(cookies: Cookies): void {
cookies.delete(RETURN_PATH_COOKIE, { path: '/' });
}
export function buildKeycloakAuthorizationUrl(systemBaseUrl: string, redirectPath: string): string {
const keycloakBaseUrl = getPublicKeycloakBaseUrl();
const redirectUri = `${systemBaseUrl}/auth/callback`;
const state = JSON.stringify({ redirect_url: redirectPath });
const params = new URLSearchParams({
client_id: getKeycloakClientId(),
redirect_uri: redirectUri,
response_type: 'code',
scope: 'openid',
prompt: 'none',
state
});
return `${keycloakBaseUrl}/realms/${getKeycloakRealm()}/protocol/openid-connect/auth?${params.toString()}`;
}
export function redirectToWorkspaceLogin(cookies: Cookies, url: URL): never {
storeWorkspaceReturnPath(cookies, url);
throw redirect(303, getWorkspaceLoginUrl(url.origin));
}
export function redirectToKeycloakAuthorization(systemBaseUrl: string, redirectPath: string): never {
throw redirect(303, buildKeycloakAuthorizationUrl(systemBaseUrl, redirectPath));
}
export function buildKeycloakLogoutUrl(systemBaseUrl: string): string {
const keycloakBaseUrl = getPublicKeycloakBaseUrl();
const workspaceLoginUrl = getWorkspaceLoginUrl(systemBaseUrl, { forPostLogout: true });
const params = new URLSearchParams({
client_id: getKeycloakClientId(),
post_logout_redirect_uri: workspaceLoginUrl
});
return `${keycloakBaseUrl}/realms/${getKeycloakRealm()}/protocol/openid-connect/logout?${params.toString()}`;
}