Merge pull request 'fix/logout-anexo' (#393) from fix/logout-anexo into development
Reviewed-on: ADUANASOFT/anexo76#393
This commit is contained in:
@@ -4,9 +4,9 @@
|
||||
*/
|
||||
|
||||
/** Por debajo de esto se usa una sola cookie `access_token` (compatibilidad). */
|
||||
export const ACCESS_TOKEN_MAX_SINGLE = 3500;
|
||||
export const ACCESS_TOKEN_MAX_SINGLE = 2800;
|
||||
|
||||
export const ACCESS_TOKEN_CHUNK_SIZE = 3500;
|
||||
export const ACCESS_TOKEN_CHUNK_SIZE = 2800;
|
||||
|
||||
/** Número de fragmentos; si existe, el token está en access_token_0..access_token_{n-1}. */
|
||||
export const ACCESS_TOKEN_CHUNK_COUNT = 'access_token_chunks';
|
||||
|
||||
@@ -180,7 +180,10 @@ async function refreshToken(): Promise<string | null> {
|
||||
if (!response.ok) {
|
||||
console.error('❌ [API] Silent refresh falló, status:', response.status);
|
||||
clearAccessTokenOnDocument();
|
||||
setTimeout(() => { window.location.href = '/login'; }, 1500);
|
||||
const hubBase = (import.meta.env.VITE_HUB_URL || 'https://workspace.aduanasoft.com').replace(/\/+$/, '');
|
||||
setTimeout(() => {
|
||||
window.location.href = `${hubBase}/login?return_to=${encodeURIComponent(window.location.origin + '/login?sso_verified=1')}`;
|
||||
}, 1500);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -662,7 +662,8 @@ export const logout = async () => {
|
||||
form.submit();
|
||||
} catch (err) {
|
||||
console.error('[auth] Error durante logout:', err);
|
||||
window.location.href = '/login';
|
||||
const hubBase = (import.meta.env.VITE_HUB_URL || 'https://workspace.aduanasoft.com').replace(/\/+$/, '');
|
||||
window.location.href = `${hubBase}/login?return_to=${encodeURIComponent(window.location.origin + '/login?sso_verified=1')}`;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -79,6 +79,7 @@ export function setAuthTokens(
|
||||
export function clearAuthTokens(cookies: Cookies) {
|
||||
clearAccessTokenCookies(cookies);
|
||||
cookies.delete('refresh_token', { path: '/' });
|
||||
cookies.delete('id_token', { path: '/' });
|
||||
cookies.delete('active_company_id', { path: '/' });
|
||||
}
|
||||
|
||||
|
||||
@@ -147,6 +147,26 @@ export function buildKeycloakAuthorizationUrl(systemBaseUrl: string, redirectPat
|
||||
return `${keycloakBaseUrl}/realms/${getKeycloakRealm()}/protocol/openid-connect/auth?${params.toString()}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construye URL de login directo en KC sin prompt=none.
|
||||
* Usa la sesión KC existente si la hay; si no, muestra el form de login.
|
||||
* Usar cuando se recibe ?redirect= del Hub (rompe el loop Hub↔login).
|
||||
*/
|
||||
export function buildKeycloakLoginUrl(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',
|
||||
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));
|
||||
@@ -156,17 +176,23 @@ export function redirectToKeycloakAuthorization(systemBaseUrl: string, redirectP
|
||||
throw redirect(303, buildKeycloakAuthorizationUrl(systemBaseUrl, redirectPath));
|
||||
}
|
||||
|
||||
export function buildKeycloakLogoutUrl(systemBaseUrl: string): string {
|
||||
export function redirectToKeycloakLogin(systemBaseUrl: string, redirectPath: string): never {
|
||||
throw redirect(303, buildKeycloakLoginUrl(systemBaseUrl, redirectPath));
|
||||
}
|
||||
|
||||
export function buildKeycloakLogoutUrl(systemBaseUrl: string, idTokenHint?: string): string {
|
||||
const keycloakBaseUrl = getPublicKeycloakBaseUrl();
|
||||
// post_logout_redirect_uri must be a URI registered in the KC client.
|
||||
// The workspace login URL (workspace.aduanasoft.com/login) is NOT registered there.
|
||||
// Use a local /auth/post-logout route which IS covered by the app's registered wildcard,
|
||||
// then that route bounces to workspace login.
|
||||
const postLogoutRedirectUri = `${systemBaseUrl}/auth/post-logout`;
|
||||
const params = new URLSearchParams({
|
||||
client_id: getKeycloakClientId(),
|
||||
post_logout_redirect_uri: postLogoutRedirectUri
|
||||
});
|
||||
|
||||
// Con id_token_hint KC acepta cualquier post_logout_redirect_uri sin necesidad
|
||||
// de que esté registrado explícitamente en el cliente.
|
||||
if (idTokenHint) {
|
||||
params.set('id_token_hint', idTokenHint);
|
||||
}
|
||||
|
||||
return `${keycloakBaseUrl}/realms/${getKeycloakRealm()}/protocol/openid-connect/logout?${params.toString()}`;
|
||||
}
|
||||
@@ -94,6 +94,16 @@ export const load: PageServerLoad = async ({ url, cookies, fetch }) => {
|
||||
maxAge: 60 * 60 * 24 * 30 // 30 días
|
||||
});
|
||||
}
|
||||
|
||||
if (tokens.id_token) {
|
||||
cookies.set('id_token', tokens.id_token, {
|
||||
path: '/',
|
||||
httpOnly: true,
|
||||
secure: isProduction,
|
||||
sameSite: 'lax',
|
||||
maxAge: 60 * 60 * 24 * 7
|
||||
});
|
||||
}
|
||||
|
||||
// Obtener la URL de redirección del state o ir al dashboard
|
||||
let redirectTo = readWorkspaceReturnPath(cookies, '/dashboard');
|
||||
|
||||
@@ -29,6 +29,7 @@ export const load: PageServerLoad = async ({ url, cookies }) => {
|
||||
const { clearAccessTokenCookies } = await import('$lib/server/access-token-cookie');
|
||||
clearAccessTokenCookies(cookies);
|
||||
cookies.delete('refresh_token', { path: '/' });
|
||||
cookies.delete('id_token', { path: '/' });
|
||||
cookies.delete('active_company_id', { path: '/' });
|
||||
}
|
||||
|
||||
@@ -76,10 +77,22 @@ export const load: PageServerLoad = async ({ url, cookies }) => {
|
||||
redirectToWorkspaceLogin(cookies, url);
|
||||
}
|
||||
|
||||
let tokens = await response.json();
|
||||
let tokens: Record<string, unknown>;
|
||||
try {
|
||||
tokens = await response.json();
|
||||
} catch (err) {
|
||||
console.error('[SSO] exchange devolvió body no-JSON (status 200):', err);
|
||||
redirectToWorkspaceLogin(cookies, url);
|
||||
}
|
||||
|
||||
if (!tokens.access_token || typeof tokens.access_token !== 'string') {
|
||||
console.error('[SSO] exchange exitoso pero access_token faltante o inválido:', tokens);
|
||||
redirectToWorkspaceLogin(cookies, url);
|
||||
}
|
||||
|
||||
console.log('[SSO] exchange exitoso, tokens recibidos:', {
|
||||
hasAccessToken: !!tokens.access_token,
|
||||
accessTokenLen: tokens.access_token?.length,
|
||||
accessTokenLen: (tokens.access_token as string).length,
|
||||
hasRefreshToken: !!tokens.refresh_token,
|
||||
tenant_id: tokens.tenant_id,
|
||||
tenant_slug: tokens.tenant_slug,
|
||||
@@ -89,7 +102,7 @@ export const load: PageServerLoad = async ({ url, cookies }) => {
|
||||
// Los tokens del relay fueron emitidos por KC via el browser (iss=IP:8085).
|
||||
// El Hub backend valida contra KC interno (hub-keycloak:8080) → issuer mismatch → 401.
|
||||
// Refrescando aquí: Anexo76 backend → Hub → KC interno → iss=hub-keycloak:8080 → válido.
|
||||
if (tokens.refresh_token) {
|
||||
if (typeof tokens.refresh_token === 'string') {
|
||||
try {
|
||||
const internalApiUrl = (
|
||||
process.env.INTERNAL_API_URL ||
|
||||
@@ -102,7 +115,7 @@ export const load: PageServerLoad = async ({ url, cookies }) => {
|
||||
body: JSON.stringify({ refresh_token: tokens.refresh_token }),
|
||||
});
|
||||
if (refreshRes.ok) {
|
||||
const refreshed = await refreshRes.json();
|
||||
const refreshed = await refreshRes.json().catch(() => ({}));
|
||||
if (refreshed.access_token && refreshed.refresh_token) {
|
||||
tokens = { ...tokens, ...refreshed };
|
||||
console.log('[SSO] tokens refrescados exitosamente (iss normalizado)');
|
||||
@@ -120,13 +133,13 @@ export const load: PageServerLoad = async ({ url, cookies }) => {
|
||||
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, {
|
||||
setAccessTokenCookies(cookies, tokens.access_token as string, {
|
||||
secure: isProduction,
|
||||
maxAge: 60 * 60 * 24 * 7,
|
||||
});
|
||||
|
||||
// refresh_token — HttpOnly (never exposed to JS)
|
||||
if (tokens.refresh_token) {
|
||||
if (typeof tokens.refresh_token === 'string') {
|
||||
cookies.set('refresh_token', tokens.refresh_token, {
|
||||
path: '/',
|
||||
httpOnly: true,
|
||||
@@ -136,10 +149,22 @@ export const load: PageServerLoad = async ({ url, cookies }) => {
|
||||
});
|
||||
}
|
||||
|
||||
// id_token — requerido para id_token_hint en el logout de Keycloak.
|
||||
// Puede venir del refresh proactivo o del exchange original.
|
||||
if (typeof tokens.id_token === 'string') {
|
||||
cookies.set('id_token', tokens.id_token, {
|
||||
path: '/',
|
||||
httpOnly: true,
|
||||
secure: isProduction,
|
||||
sameSite: 'lax',
|
||||
maxAge: 60 * 60 * 24 * 7,
|
||||
});
|
||||
}
|
||||
|
||||
// sso_tenant_id — HttpOnly cookie con el tenant seleccionado.
|
||||
// El backend lo pasa como X-Tenant-Override en Hub /auth/me para que
|
||||
// devuelva el tenant correcto aunque el KC token tenga otro tenant baked in.
|
||||
if (tokens.tenant_id) {
|
||||
if (typeof tokens.tenant_id === 'number') {
|
||||
cookies.set('sso_tenant_id', String(tokens.tenant_id), {
|
||||
path: '/',
|
||||
httpOnly: true,
|
||||
@@ -171,7 +196,7 @@ export const load: PageServerLoad = async ({ url, cookies }) => {
|
||||
await fetch(`${internalApiUrl}/v1/auth/lazy-link`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${tokens.access_token}`,
|
||||
'Authorization': `Bearer ${tokens.access_token as string}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
}).catch(() => {});
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { getServerApiUrl, getAuthTokens } from '$lib/server/api';
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import { redirectToWorkspaceLogin } from '$lib/server/workspace-auth';
|
||||
|
||||
export const load: PageServerLoad = async ({ cookies, fetch, url, parent }) => {
|
||||
const parentData = await parent();
|
||||
const { accessToken } = getAuthTokens(cookies);
|
||||
if (!accessToken) {
|
||||
throw redirect(302, '/login');
|
||||
redirectToWorkspaceLogin(cookies, url);
|
||||
}
|
||||
|
||||
const page = Number(url.searchParams.get('page')) || 1;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { getServerApiUrl, getAuthTokens } from '$lib/server/api';
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import { redirectToWorkspaceLogin } from '$lib/server/workspace-auth';
|
||||
|
||||
export const load: PageServerLoad = async ({ cookies, fetch, url, parent }) => {
|
||||
const parentData = await parent();
|
||||
const { accessToken } = getAuthTokens(cookies);
|
||||
if (!accessToken) {
|
||||
throw redirect(302, '/login');
|
||||
redirectToWorkspaceLogin(cookies, url);
|
||||
}
|
||||
|
||||
const page = Number(url.searchParams.get('page')) || 1;
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { error, redirect } from '@sveltejs/kit';
|
||||
import { getAuthTokens, getActiveCompanyId, authenticatedFetch } from '$lib/server/api';
|
||||
import { redirectToWorkspaceLogin } from '$lib/server/workspace-auth';
|
||||
|
||||
export const load: PageServerLoad = async ({ params, cookies, fetch }) => {
|
||||
export const load: PageServerLoad = async ({ params, cookies, fetch, url }) => {
|
||||
const { accessToken } = getAuthTokens(cookies);
|
||||
|
||||
if (!accessToken) {
|
||||
throw redirect(302, '/login');
|
||||
redirectToWorkspaceLogin(cookies, url);
|
||||
}
|
||||
|
||||
const companyId = await getActiveCompanyId(cookies, fetch);
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import {
|
||||
getAuthTokens,
|
||||
authenticatedFetch
|
||||
import {
|
||||
getAuthTokens,
|
||||
authenticatedFetch
|
||||
} from '$lib/server/api';
|
||||
import { redirectToWorkspaceLogin } from '$lib/server/workspace-auth';
|
||||
|
||||
export const load: PageServerLoad = async ({ fetch, cookies, url, parent }) => {
|
||||
const invoiceType = url.searchParams.get('invoice_type');
|
||||
@@ -21,7 +22,7 @@ export const load: PageServerLoad = async ({ fetch, cookies, url, parent }) => {
|
||||
const accessToken = tokens.accessToken;
|
||||
|
||||
if (!accessToken) {
|
||||
throw redirect(302, '/login');
|
||||
redirectToWorkspaceLogin(cookies, url);
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { error, redirect } from '@sveltejs/kit';
|
||||
import { getAuthTokens, getActiveCompanyId, authenticatedFetch } from '$lib/server/api';
|
||||
import { redirectToWorkspaceLogin } from '$lib/server/workspace-auth';
|
||||
|
||||
export const load: PageServerLoad = async ({ params, cookies, fetch, url }) => {
|
||||
const { accessToken } = getAuthTokens(cookies);
|
||||
|
||||
if (!accessToken) {
|
||||
throw redirect(302, '/login');
|
||||
redirectToWorkspaceLogin(cookies, url);
|
||||
}
|
||||
|
||||
// Obtener el company_id de la cookie
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { error, redirect } from '@sveltejs/kit';
|
||||
import { getAuthTokens, getActiveCompanyId, authenticatedFetch } from '$lib/server/api';
|
||||
import { redirectToWorkspaceLogin } from '$lib/server/workspace-auth';
|
||||
|
||||
export const load: PageServerLoad = async ({ cookies, fetch, url }) => {
|
||||
const { accessToken } = getAuthTokens(cookies);
|
||||
|
||||
if (!accessToken) {
|
||||
throw redirect(302, '/login');
|
||||
redirectToWorkspaceLogin(cookies, url);
|
||||
}
|
||||
|
||||
const companyId = await getActiveCompanyId(cookies, fetch);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import { getAuthTokens, authenticatedFetch } from '$lib/server/api';
|
||||
import { redirectToWorkspaceLogin } from '$lib/server/workspace-auth';
|
||||
|
||||
export const load: PageServerLoad = async ({ fetch, cookies, url, parent }) => {
|
||||
// Esperar a que el layout padre valide/refresque el token
|
||||
@@ -10,7 +11,7 @@ export const load: PageServerLoad = async ({ fetch, cookies, url, parent }) => {
|
||||
const { accessToken } = getAuthTokens(cookies);
|
||||
|
||||
if (!accessToken) {
|
||||
throw redirect(302, '/login');
|
||||
redirectToWorkspaceLogin(cookies, url);
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { error, redirect } from '@sveltejs/kit';
|
||||
import { getAuthTokens, getActiveCompanyId, authenticatedFetch } from '$lib/server/api';
|
||||
import { redirectToWorkspaceLogin } from '$lib/server/workspace-auth';
|
||||
|
||||
export const load: PageServerLoad = async ({ params, cookies, fetch }) => {
|
||||
export const load: PageServerLoad = async ({ params, cookies, fetch, url }) => {
|
||||
const { accessToken } = getAuthTokens(cookies);
|
||||
|
||||
if (!accessToken) {
|
||||
throw redirect(302, '/login');
|
||||
redirectToWorkspaceLogin(cookies, url);
|
||||
}
|
||||
|
||||
// Obtener el company_id de la cookie para ambas ramas (new y edit)
|
||||
@@ -84,7 +85,7 @@ export const load: PageServerLoad = async ({ params, cookies, fetch }) => {
|
||||
throw error(404, 'Pedimento no encontrado');
|
||||
}
|
||||
if (response.status === 401) {
|
||||
throw redirect(302, '/login');
|
||||
redirectToWorkspaceLogin(cookies, url);
|
||||
}
|
||||
if (response.status === 403) {
|
||||
throw error(403, 'No tiene permiso para editar pedimentos');
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import { getAuthTokens } from '$lib/server/api';
|
||||
import { redirectToWorkspaceLogin } from '$lib/server/workspace-auth';
|
||||
|
||||
export const load: PageServerLoad = async ({ cookies }) => {
|
||||
export const load: PageServerLoad = async ({ cookies, url }) => {
|
||||
const { accessToken } = getAuthTokens(cookies);
|
||||
|
||||
if (!accessToken) {
|
||||
throw redirect(302, '/login');
|
||||
redirectToWorkspaceLogin(cookies, url);
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import { getAuthTokens } from '$lib/server/api';
|
||||
import { redirectToWorkspaceLogin } from '$lib/server/workspace-auth';
|
||||
|
||||
export const load: PageServerLoad = async ({ cookies }) => {
|
||||
export const load: PageServerLoad = async ({ cookies, url }) => {
|
||||
const { accessToken } = getAuthTokens(cookies);
|
||||
|
||||
if (!accessToken) {
|
||||
throw redirect(302, '/login');
|
||||
redirectToWorkspaceLogin(cookies, url);
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -5,10 +5,12 @@ import {
|
||||
getWorkspaceLoginUrl,
|
||||
readWorkspaceReturnPath,
|
||||
storeReturnPath,
|
||||
redirectToKeycloakAuthorization
|
||||
redirectToKeycloakAuthorization,
|
||||
redirectToKeycloakLogin
|
||||
} from '$lib/server/workspace-auth';
|
||||
|
||||
export const load: PageServerLoad = async ({ cookies, url }) => {
|
||||
console.error('[LOGIN] url.pathname:', url.pathname, '| params:', Object.fromEntries(url.searchParams));
|
||||
clearAuthTokens(cookies);
|
||||
|
||||
// Workspace redirige de vuelta aquí con ?sso_verified=1 después de que el usuario
|
||||
@@ -26,12 +28,22 @@ export const load: PageServerLoad = async ({ cookies, url }) => {
|
||||
redirectToKeycloakAuthorization(url.origin, intendedPath);
|
||||
}
|
||||
|
||||
// Sin sso_verified → primera visita o sesión expirada.
|
||||
// Guardar la ruta deseada y mandar al Workspace a autenticar.
|
||||
const intendedPath = url.searchParams.get('redirect') || '/dashboard';
|
||||
if (intendedPath !== '/dashboard') {
|
||||
// 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));
|
||||
};
|
||||
|
||||
@@ -1,18 +1,31 @@
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import type { RequestHandler } from './$types';
|
||||
import { clearAccessTokenCookies } from '$lib/server/access-token-cookie';
|
||||
import { buildKeycloakLogoutUrl, clearWorkspaceReturnPath } from '$lib/server/workspace-auth';
|
||||
import {
|
||||
buildKeycloakLogoutUrl,
|
||||
clearWorkspaceReturnPath,
|
||||
getWorkspaceLoginUrl
|
||||
} from '$lib/server/workspace-auth';
|
||||
|
||||
export const POST: RequestHandler = async ({ cookies, request, url }) => {
|
||||
export const POST: RequestHandler = async ({ cookies, url }) => {
|
||||
const systemBaseUrl = url.origin;
|
||||
|
||||
// Eliminar todas las cookies de autenticación (access_token puede estar fragmentado)
|
||||
const idToken = cookies.get('id_token');
|
||||
|
||||
// Eliminar todas las cookies de autenticación
|
||||
clearAccessTokenCookies(cookies);
|
||||
cookies.delete('refresh_token', { path: '/' });
|
||||
cookies.delete('id_token', { path: '/' });
|
||||
cookies.delete('active_company_id', { path: '/' });
|
||||
cookies.delete('sso_tenant_id', { path: '/' });
|
||||
cookies.delete('sso_tenant_pub', { path: '/' });
|
||||
clearWorkspaceReturnPath(cookies);
|
||||
|
||||
throw redirect(303, buildKeycloakLogoutUrl(systemBaseUrl));
|
||||
// Sin id_token_hint KC rechaza post_logout_redirect_uri no registrado.
|
||||
// En ese caso redirigir directo al workspace — las cookies ya están limpias.
|
||||
if (!idToken) {
|
||||
throw redirect(303, getWorkspaceLoginUrl(systemBaseUrl, { forPostLogout: true }));
|
||||
}
|
||||
|
||||
throw redirect(303, buildKeycloakLogoutUrl(systemBaseUrl, idToken));
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user