Skip to content

Suplantación

Audience

Este artículo es para operadores de la plataforma GCM. Los administradores de iglesia deberían ver Usuarios y roles — la suplantación está solo disponible para el personal con la reclamación platform_admin o superadmin, y es la acción más auditada en la plataforma.

La suplantación permite a un usuario del personal entrar al espacio de trabajo de un cliente como si fuera el administrador de esa organización, para que pueda reproducir un bug, ejecutar una corrección puntual o guiar a un cliente por una pantalla en una llamada. Es una característica regular, no una puerta trasera — cada entrada y salida se captura en platform_audit_log, se delimita a una clave de sessionStorage (no localStorage, no una cookie) y termina automáticamente cuando se cierra la pestaña.

Traspaso de suplantación desde la hoja de detalle de organización

Cómo iniciar una sesión

Desde la lista de Organizaciones, abre la hoja de detalle de cualquier fila (el chevron al final de la fila, o Abrir desde la tabla de operaciones de facturación). La hoja del lado derecho muestra plan, estado, personal y módulos. El botón azul Abrir cuenta en la parte superior de la fila de acciones es el punto de entrada.

Haz clic en él, y tres cosas suceden en orden:

  1. Un diálogo de confirmación pide un motivo de texto libre. Esto es obligatorio.
  2. El frontend llama al RPC de Postgres start_impersonation con el id de la organización objetivo y el motivo.
  3. Si el RPC tiene éxito, se crea la fila en admin_impersonation, el id de la organización se escribe en sessionStorage bajo la clave gcm_impersonation, la caché de React Query se limpia (para evitar fuga de datos cross-org) y el usuario es redirigido a / — que ahora se resuelve a la capa del inquilino del cliente.

Aparece un banner rojo que dice "Viendo como ... (suplantación)" en la parte superior de la capa del inquilino. Las páginas de administrador del cliente se renderizan como si fueras ellos: su panel, sus miembros, sus donaciones. Puedes hacer clic en cualquier botón en el que su administrador podría hacer clic.

Un objetivo a la vez

Iniciar una nueva suplantación mientras una está en progreso está bloqueado por el RPC. Termina la sesión actual primero. El comportamiento es intencional — la tabla de auditoría asume una fila activa por actor.

Para qué es el motivo

El campo de motivo es obligatorio y termina en tres lugares:

  • La columna admin_impersonation.reason.
  • La fila de platform_audit_log con action = 'impersonation_started'.
  • El KPI de "Suplantaciones" de 7 días en la pestaña del registro de auditoría.

Escribe algo que un auditor futuro pueda leer. "Ticket de soporte #4421 — el donante ve un 500 en el formulario de donaciones" es bueno. "verificando" no lo es. El auditor eres tú, en 90 días, después de que el cliente se queje.

Cómo RLS te mantiene delimitado

Mientras estás suplantando, el helper SQL current_org_id() devuelve el id de la organización objetivo en lugar del tuyo. Cada política RLS en la plataforma usa current_org_id() (no las reclamaciones JWT crudas), por lo que las lecturas y escrituras se delimitan silenciosamente a los datos del cliente. Puedes leer sus miembros porque la política pasa; no puedes leer los miembros de otra organización porque el helper no devuelve su id.

Por eso usamos current_org_id() y no auth.jwt() ->> 'organization_id' en las políticas. Lo segundo se filtraría a través de la suplantación.

Hay una red de seguridad adicional: las edge functions leen el contexto de suplantación a través de getCallerContext para que ellas también sepan que están corriendo bajo el id de un cliente, no el tuyo. Los registros de auditoría incluyen tanto actor_id (tú) como target_org_id (el cliente) para que exista un rastro forense de cualquier manera.

Cómo termina una sesión

Una sesión termina de tres maneras:

  1. Regresas al modo plataforma — haz clic en Regresar a la plataforma en el banner. El frontend llama a end_impersonation, limpia sessionStorage, limpia la caché de React Query, escribe una fila de auditoría impersonation_ended y te empuja a /platform.
  2. Cierras la pestaña — sessionStorage se borra automáticamente. La fila de admin_impersonation permanece abierta hasta que vuelvas a iniciar sesión, pero el alcance del JWT se ha ido y el próximo inicio de sesión del usuario del personal la cierra. Razón por la que este diseño existe: un dispositivo robado no puede reanudar una sesión reabriendo la pestaña.
  3. Cierras sesión — lo mismo que cerrar la pestaña desde el punto de vista de la suplantación.

La acumulación (suplantar, actualizar la pestaña, suplantar a alguien más) está bloqueada porque la nueva llamada RPC termina la fila anterior antes de crear una nueva.

Qué deberías y no deberías hacer

Hacer

  • Reproducir el bug que el cliente reportó, luego regresar.
  • Guiar a un cliente por una pantalla en una llamada — ellos ven tu cursor, tú ves sus datos.
  • Ejecutar una corrección puntual documentada (por ejemplo, sembrar un fondo predeterminado faltante) cuando hayan aprobado el cambio por escrito.

No hacer

  • Editar configuración que el cliente no esperaría que tocaras. Si no estás seguro, pregunta primero y enlaza la solicitud en el campo de motivo.
  • Enviar mensajes, publicar a canales o disparar workflows como el cliente. El destinatario ve el nombre y número de la organización, no los tuyos — cualquier cosa que envíes se atribuye permanentemente a ellos.
  • Permanecer en suplantación más tiempo del que toma la tarea. El banner es visible para cualquiera que pase por tu monitor.

Leyendo el rastro de auditoría de suplantación

La pestaña Registro de auditoría lleva un KPI de 7 días para suplantaciones y el AuditLogViewer te permite filtrar por acción. Tres acciones importan:

  • impersonation_started — inicio exitoso. Los metadatos incluyen target_org_id, org_name, reason.
  • impersonation_ended — salida limpia vía Regresar a la plataforma.
  • impersonation_failed — el RPC start_impersonation rechazó la llamada. Los metadatos incluyen el error subyacente. Un intento fallido normalmente es un mal disparo de RLS, una organización objetivo que ya no existe, o una cuenta de personal que acaba de perder su reclamación.

Filtra por actor_email = <tú> para ver tus propias sesiones durante los últimos 7 días. Filtra por metadata->>'target_org_id' = <id> para ver cada sesión del personal contra un cliente — una respuesta útil a "¿alguien de tu equipo ha iniciado sesión en nuestra cuenta recientemente?".

Cuando algo se siente mal

Si llegas a una página de inquilino y los datos no se ven como los del cliente — nombres de miembros incorrectos, idioma incorrecto, logo de organización incorrecto — regresa al modo plataforma inmediatamente. Dos modos de falla pueden causar esto:

  1. Caché obsoleta — la caché de React Query sobrevivió la entrada. Refresca con fuerza la página; si eso lo arregla, el bug está en la ruta de entrada y vale la pena un issue de Sentry.
  2. Fila de suplantación atascada — tu sesión anterior no se cerró limpiamente y current_org_id() está devolviendo el antiguo objetivo. Cierra sesión completamente, vuelve a iniciar sesión, y el auth-hook reseteará el JWT.

En ambos casos, escribe una entrada de auditoría después explicando lo que viste. El punto entero de que la superficie de suplantación sea auditable es que aprendemos de los casos límite.