Administracion global de sesiones
Sesiones activas
Para que sirve esta pantalla
/super-admin/sessions permite consultar sesiones del sistema y cerrar
sesiones de forma administrativa. Sirve para reaccionar ante accesos no deseados,
sesiones antiguas, usuarios que cambiaron de rol, soporte operativo o incidentes de
seguridad.
Esta pantalla no usa el endpoint legacy /api/auth/forze-close. Usa
endpoints Super Admin protegidos por Token.Auth y
requireRole(‘super_admin’).
RutaFrontend/super-admin/sessionsComponenteVista principalActiveSessions.vueEndpointListadoGET /api/super-admin/sessions Proceso al cargar la pantalla
- El router entra a
/super-admin/sessions con meta.role = ‘super_admin’. - El layout Super Admin muestra el item Sesiones activas.
ActiveSessions.vue ejecuta mounted().- Primero carga negocios para el filtro con
GET /api/super-admin/businesses?page=1&pageSize=100. - Despues carga sesiones con
GET /api/super-admin/sessions. - El backend valida token, sesion activa si aplica y rol
super_admin. - El DAO consulta
session, login, rol y business. - La vista pinta tabla, paginacion, filtros y acciones disponibles.
Filtros, paginacion y ordenamiento
| Control | Parametro | Que hace | Notas |
|---|
| Buscar sesion | search | Busca por id de sesion, nombre, correo, id de login, rol o negocio. | Usa debounce de 450 ms antes de consultar backend. |
| Negocio | businessId | Filtra sesiones asociadas a un negocio. | El filtro se carga desde el listado de negocios Super Admin. |
| Rol | role | Filtra por codigo de rol. | Opciones visibles: super_admin, admin, business_admin, doctor, assistant, reception, receptionist. |
| Estado | active | Permite ver activas, inactivas o todas. | Por defecto la UI usa true. |
| Por pagina | pageSize | Cambia tamano de pagina. | Opciones: 10, 20, 50, 100. |
| Orden | sortBy, sortDir | Ordena server-side por columnas permitidas. | Whitelist: created_at, updated_at, active, last, user_name, email, role, business_name. |
Columnas de la tabla: que significa cada una
| Columna | Fuente | Significado operativo | Como interpretarla |
|---|
| Usuario | login.name | Nombre de la cuenta de acceso. | No es paciente. Es usuario del sistema. |
| Correo | login.email | Correo de la cuenta. | Sirve para identificar al usuario antes de cerrar sesiones. |
| Rol | rol.code | Rol tecnico/operativo del usuario. | Ayuda a distinguir super_admin, admin tenant, doctor o auxiliar. |
| Negocio | business.name | Tenant asociado al usuario. | Super Admin puede aparecer sin negocio. |
| Activa | session.active | Indica si la sesion esta activa en backend. | Si es false, no deberia permitir operar con sesiones activas obligatorias. |
| Ultima | session.last | Marca la sesion vigente principal del usuario. | El middleware exige active = 1 y last = 1. |
| Inicio | session.created_at | Fecha de creacion de la sesion. | Permite detectar sesiones antiguas. |
| Actualizacion | session.updated_at | Ultimo cambio de la sesion. | Cambia al cerrar o actualizar la sesion. |
| Cierre forzado | session.forze_at | Fecha de cierre administrativo. | Si esta vacia, no hay cierre forzado registrado. |
| Forzada por | session.forze_by | Usuario Super Admin que forzo el cierre. | Sirve para trazabilidad operativa. |
| Acciones | UI + endpoints Super Admin | Cerrar una sesion o todas las sesiones del usuario. | No se muestran acciones para sesion propia ni para sesiones cerradas. |
Boton: Cerrar sesion
Cierra una sesion especifica. La UI abre un modal de confirmacion y llama:
PATCH /api/super-admin/sessions/:id/close
En frontend, confirmCloseSession() llama
DASuperAdminService.closeSession(session.id_session). En backend, la
ruta llama SuperAdminController.closeSession y despues
SuperAdminDao.closeSession.
Boton: Cerrar sesiones del usuario
Cierra todas las sesiones activas del usuario seleccionado. Requiere escribir
exactamente CERRAR antes de confirmar.
PATCH /api/super-admin/users/:id/close-sessions
En frontend, confirmCloseUserSessions() llama
DASuperAdminService.closeUserSessions(idLogin). En backend, la ruta
llama SuperAdminController.closeUserSessions y luego
SuperAdminDao.closeUserSessions.
SQL de listado de sesiones
El listado une sesiones con usuarios, roles y negocios. No consulta pacientes,
expedientes, recetas, citas clinicas, tratamientos ni odontograma.
SELECT
s.id_session,
s.active,
s.last,
s.created_at,
s.updated_at,
s.forze_at,
s.forze_by,
l.id_login,
l.name AS user_name,
l.email,
r.code AS role,
b.id_business,
b.name AS business_name
FROM `session` s
INNER JOIN login l ON l.id_login = s.id_login
LEFT JOIN rol r ON r.id_rol = l.id_rol
LEFT JOIN business b ON b.id_business = l.id_business
WHERE ...filtros seguros...
ORDER BY ...columna permitida...
LIMIT :_limit OFFSET :_offset;
Que cambia en base de datos al cerrar una sesion
El cierre de una sesion especifica marca esa sesion como inactiva y tambien invalida
refresh tokens activos del usuario. Esto es importante: aunque el boton cierre una
sesion puntual, el backend apaga los refresh tokens activos del usuario para evitar que
pueda emitir nuevos access tokens con una sesion cerrada.
UPDATE `session`
SET active = 0,
last = 0,
forze_by = :actor_login_id,
forze_at = NOW(),
updated_by = :actor_login_id,
updated_at = NOW()
WHERE id_session = :id_session;
UPDATE refresh_token
SET last = 0
WHERE id_login = :id_login
AND last = 1;
Que cambia al cerrar todas las sesiones de un usuario
El cierre masivo busca todas las sesiones activas del usuario, las marca como cerradas
y apaga sus refresh tokens activos. No borra usuario, negocio ni datos.
UPDATE `session`
SET active = 0,
last = 0,
forze_by = :actor_login_id,
forze_at = NOW(),
updated_by = :actor_login_id,
updated_at = NOW()
WHERE id_login = :id_login
AND active = 1;
UPDATE refresh_token
SET last = 0
WHERE id_login = :id_login
AND last = 1;
Como se refleja en el usuario
| Escenario | Con REQUIRE_ACTIVE_SESSION=true | Con REQUIRE_ACTIVE_SESSION=false | Refresh token |
|---|
| Usuario con access token vigente | El siguiente request protegido falla porque la sesion ya no esta activa. | El access token puede seguir funcionando hasta expirar. | No puede renovar si su refresh activo fue apagado. |
| Usuario con “mantener sesion” | Queda fuera en el siguiente request y no puede refrescar token. | Puede conservar access token hasta expiracion, pero no renovar. | refresh_token.last = 0. |
| Usuario sin “mantener sesion” | Queda fuera en el siguiente request protegido. | Access token vive hasta expiracion. | No tenia refresh token util que renovar. |
| Sesion ya cerrada | No se muestra boton; aparece como cerrada. | No se muestra boton; aparece como cerrada. | Debe estar invalidado si fue cierre forzado. |
Validacion de sesion activa en cada request
Cuando REQUIRE_ACTIVE_SESSION=true, el middleware
awaitHandlerFactory revisa que el JWT tenga id_login e
id_session. Luego consulta SessionDao.checkActiveSession().
Solo permite continuar si existe una fila en session con
active = 1 y last = 1.
SELECT id_session
FROM `session`
WHERE id_login = :id_login
AND id_session = :id_session
AND active = 1
AND last = 1
LIMIT 1;
Duracion de sesion y access token
La duracion del access token se configura con JWT_EXPIRES_IN en
src/config/security.js y se aplica desde src/config/jwt.js.
En produccion controlada se usa JWT_EXPIRES_IN=3600, equivalente a 1
hora. Si no se configura, el default del codigo es 60000.
Cerrar una sesion no cambia la fecha interna del JWT ya emitido; lo que hace efectivo
el cierre inmediato es REQUIRE_ACTIVE_SESSION=true, porque obliga a validar
la sesion en base de datos en cada request protegido.
Auditoria de cierres
| Accion | Evento | Metadata segura |
|---|
| Cerrar una sesion | SUPER_ADMIN_SESSION_CLOSE | Scope de invalidacion refresh y razon super_admin_close_session. |
| Cerrar sesiones de usuario | SUPER_ADMIN_USER_SESSIONS_CLOSE | IDs de sesiones cerradas, scope refresh y razon super_admin_close_user_sessions. |
La auditoria no debe guardar passwords, hashes, tokens, refresh tokens ni datos
clinicos. Los cierres tampoco borran negocio, usuario ni informacion clinica.
Diagrama de cierre de sesion
flowchart TD
A[Super Admin pulsa Cerrar sesion] --> B[Modal de confirmacion]
B --> C[PATCH /api/super-admin/sessions/:id/close]
C --> D[Token.Auth]
D --> E[requireRole super_admin]
E --> F[SuperAdminController.closeSession]
F --> G[SuperAdminDao.closeSession]
G --> H{Es sesion propia?}
H -- Si --> I[403: no permitido]
H -- No --> J[UPDATE session active=0 last=0]
J --> K[UPDATE refresh_token last=0]
K --> L[Auditoria SUPER_ADMIN_SESSION_CLOSE]
L --> M[Frontend refresca tabla]
M --> N[Usuario afectado falla en siguiente request si REQUIRE_ACTIVE_SESSION=true]
Diagrama de cierre de sesiones del usuario
flowchart TD
A[Super Admin pulsa Cerrar sesiones del usuario] --> B[Debe escribir CERRAR]
B --> C[PATCH /api/super-admin/users/:id/close-sessions]
C --> D[Token.Auth + requireRole super_admin]
D --> E[SuperAdminController.closeUserSessions]
E --> F[SuperAdminDao.closeUserSessions]
F --> G{Es el propio usuario?}
G -- Si --> H[403: no permitido]
G -- No --> I[Buscar usuario]
I --> J[Buscar sesiones activas]
J --> K[UPDATE session active=0 last=0 WHERE id_login]
K --> L[UPDATE refresh_token last=0 WHERE id_login]
L --> M[Auditoria SUPER_ADMIN_USER_SESSIONS_CLOSE]
M --> N[Frontend refresca tabla]