AACsearch

Autenticación y Multi-tenencia

Cómo funcionan las organizaciones como workspaces, cómo fluye el contexto de sesión a través de la API y el modelo de seguridad para búsqueda multi-tenant.

AACsearch es multi-tenant por diseño. Cada recurso — índices de búsqueda, API keys, eventos de uso, espacios de conocimiento — está limitado a una organización. Las lecturas entre organizaciones nunca están permitidas.

Stack de autenticación

La autenticación es proporcionada por Better Auth configurado en packages/auth/auth.ts.

Características habilitadas:

CaracterísticaNotas
Correo electrónico + contraseñaFlujo de inicio de sesión estándar
Magic linksInicio de sesión sin contraseña por correo electrónico
PasskeysWebAuthn
2FA (TOTP)Contraseñas de un solo uso basadas en tiempo
OAuth (Google, GitHub)Inicio de sesión social
OrganizacionesWorkspaces con membresía basada en roles
Organizaciones solo por invitaciónOpcional; configurable
Panel de administraciónGestión interna de usuarios/organizaciones
Suplantación de sesiónPara soporte/depuración

Las organizaciones como workspaces

Una organización es la unidad principal del workspace. Los usuarios pueden pertenecer a múltiples organizaciones y cambiar entre ellas en el panel. Cada índice de búsqueda, API key y evento de analíticas pertenece exactamente a una organización.

Usuario → Member(rol) → Organización

                     SearchIndex(es)
                     SearchApiKey(s)
                     SearchUsageEvent(s)
                     KnowledgeSpace(s)

Contexto de sesión en oRPC

Los procedimientos oRPC se llaman como uno de tres tipos:

Tipo de procedimientoContexto disponibleUsado para
publicProcedureNo se requiere sesiónManejador de búsqueda pública, verificación de salud
protectedProcedurecontext.user, context.sessionOperaciones autenticadas del panel
adminProcedurecontext.user + verificación de rol de administradorOperaciones solo de administrador

Acceso a la sesión en un componente de servidor:

import { getSession } from "@auth/lib/server";

const session = await getSession();

Acceso a la sesión en un componente cliente:

"use client";
import { useSession } from "@auth/hooks/use-session";

const { user, loaded } = useSession();

Acceso a la organización activa:

"use client";
import { useActiveOrganization } from "@organizations/hooks/use-active-organization";

const { activeOrganization, isOrganizationAdmin } = useActiveOrganization();

Aislamiento de tenant — Invariante duro

Cada llamada de búsqueda DEBE pasar tenantId: verified.organizationId. Esto se aplica en packages/api/modules/search/public-handler.ts:

// Siempre combinar con AND con el filtro de tenant
const tenantFilter = `organization_id:=${organizationId}`;
const combinedFilter = combineFilters(tenantFilter, callerFilter);

No existe ninguna ruta de código que devuelva documentos entre organizaciones. Si está escribiendo un nuevo procedimiento de búsqueda, este invariante debe mantenerse explícitamente — no es automático.

Modelo de seguridad de las API keys

Las API keys son el mecanismo de autenticación principal para los llamadores externos (módulos CMS y SDKs del navegador).

Tipos de claves y prefijos

PrefijoÁmbitoUsado por
ss_search_*searchSDK del navegador, widget — solo lectura
ss_connector_*connector_writeMódulos CMS — escritura + sincronización
ss_scoped_*Reducido desde ss_search_*Tokens por usuario, por filtro

Garantías de seguridad

  • Almacenamiento solo hash: SearchApiKey.hashedKey almacena solo el hash bcrypt. El texto plano se muestra una vez en la creación y nunca se registra, nunca se devuelve en operaciones de lista y nunca se repite.
  • Restricción de origen: Cada clave tiene allowedOrigins[]. Las solicitudes de orígenes no incluidos en la lista se rechazan a nivel de API antes de cualquier llamada a AACSearch. - Límite de velocidad: Ventana deslizante por clave aplicada mediante SearchRateLimitBucket.
  • Cuota: Cuota del plan por organización aplicada mediante el middleware quotaCheck antes de búsqueda/ingestión.
  • Expiración: Las claves pueden tener expiresAt; las claves expiradas se rechazan y el trabajo de mantenimiento las limpia.

Aislamiento de clave de administrador

La clave de administrador de AACSearch nunca sale del servidor. Vive en packages/search/lib/client.ts, instanciada una vez desde la variable de entorno AACSEARCH_API_KEY. Nunca se devuelve a ningún cliente, nunca se pasa a módulos CMS y nunca se incrusta en el bundle del widget.

Los módulos CMS reciben solo tokens ss_connector_* que pasan por la API de conector de AACsearch, que luego usa la clave de administrador internamente.

Tokens con ámbito

Los tokens con ámbito reducen los permisos de una clave ss_search_* existente. Son JWT sin estado firmados con HMAC (no almacenados en la base de datos) emitidos por issueScopedSearchToken().

Un caso de uso común: emitir un token por usuario que limite los resultados solo a los productos de ese usuario.

// Lado del servidor: emitir un token con ámbito para un rango de precios específico
const token = await orpc.search.createScopedToken.call({
	organizationId,
	indexSlug: "products",
	scopedFilter: "price:<100",
	expiresInSeconds: 3600,
});

El scopedFilter siempre se combina con AND con los propios filtros del llamador mediante combineFilters(). Solo puede reducir permisos — no puede ampliarlos. Omitir combineFilters es una violación del Invariante Duro.

Lista de permisiones de origen por clave

Cada clave ss_search_* tiene un array allowedOrigins[]. Solo pasan las solicitudes de esos orígenes.

En el plan gratuito, solo se permiten subdominios de aacsearch.com. Pro y superior admiten orígenes personalizados. Consulte Planes y límites para la política por plan.

Acceso basado en roles dentro de las organizaciones

RolCapacidades
ownerAcceso completo, puede transferir organización, gestionar facturación
adminGestionar índices, claves, miembros, configuración
memberVer panel, ejecutar búsquedas en vista previa

Las verificaciones de roles en los procedimientos oRPC usan el objeto context.session proporcionado por Better Auth.

On this page