Invitaciones
ARGUS tiene dos flujos de invitación distintos. Ambos usan tokens firmados de un solo uso y ambos aterrizan en la misma ruta /invite, pero difieren en alcance y duración.
Los dos tipos de invitación
Invitaciones de organización — cuentas completas
Una invitación de organización añade un usuario con un OrgRole real (admin, manager, operator, observer). Las invitaciones aceptadas crean una membresía permanente en orgs/{orgId}/members/{uid}.
- Quién envía: cualquiera con
admin.users(rolessuperadmin,admin,manager). - Duración: 7 días por defecto, configurable por invitación.
- Desde: Admin → Usuarios (
/admin/users).
Invitaciones de invitado a misión
Un enlace de corta duración que da acceso a una sola operación — sin membresía en organización, sin otras misiones, sin superficies de administración. Útil para compartir una operación con una agencia externa durante unas horas.
- Quién envía: propietario de la misión o admin de la organización.
- Duración: 1 h / 12 h / 24 h / 7 d.
- Desde: Detalle de misión → Compartir (
/operations/:id). - Acceso: solo lectura por defecto; conmutadores opcionales “puede anotar” / “puede transmitir por PTT”.
Los invitados inician sesión o se registran para esa única operación; cuando el enlace expira o es revocado, pierden acceso inmediatamente.
Enviar una invitación de organización
- Ve a
/admin/users, haz clic en Invitar usuario. - Rellena Correo, Rol (integrado o personalizado si tu plan lo soporta), expira en opcional (por defecto 7 días), mensaje opcional.
- Haz clic en Enviar. El servidor crea un
OrgInvitation+InviteTokenDoccon el token opaco, y luego envía el correo.
La invitación pendiente aparece en la lista de usuarios con una insignia gris “Pendiente” — desde ahí puedes Copiar enlace, Reenviar o Revocar.
Enviar una invitación de invitado a misión
- Abre la misión en
/operations/:id. - Haz clic en Compartir.
- Elige Como invitado (frente a añadir un usuario existente como participante).
- Introduce el correo, establece la expiración, elige un preset de permisos y haz clic en Enviar.
El documento de la misión recibe una nueva entrada en su subcolección guestInvites y el correo sale con la misma forma de URL /invite?token=... — el token lleva si es una invitación de organización o una invitación de invitado a misión.
Canjear una invitación
El correo de invitación contiene un enlace como https://app.argus.tactical/invite?token=<opaque>. Abrirlo enruta a InviteComponent en /invite, que:
- Verifica el token. Estados:
loading→ready/invalid/expired/revoked/accepted. - Muestra la oferta — quien invita, nombre de la organización, rol y fecha de expiración.
- Se ramifica según la autenticación:
- Ya has iniciado sesión — muestra Aceptar invitación.
InvitationService.acceptInvite()ejecuta la transacción en el servidor, crea la membresía y te lleva al dashboard. - No has iniciado sesión — mini formulario de autenticación pre-rellenado con el correo de la invitación. Alterna entre Iniciar sesión y Crear cuenta; al enviarlo, te autentica y acepta en un solo paso (
loginAndAccept()/registerAndAccept()).
- Ya has iniciado sesión — muestra Aceptar invitación.
- El estado “Bienvenido a {org}” se renderiza con un botón Entrar al Dashboard.
Qué puede salir mal
- Inválida — el token no existe / está malformado.
- Expirada — pasó
expiresOn. - Revocada — el remitente la revocó antes de que la aceptaras.
- Ya aceptada — ya la has usado antes.
- Error — fallo del lado del servidor durante
acceptInvite; el componente ofrece Reintentar.
El correo de la invitación es autoritativo — aceptar mientras has iniciado sesión con un usuario distinto se rechaza.
Revocar una invitación
Desde /admin/users → Invitaciones pendientes → acciones de fila → Revocar. Revocar escribe status: 'revoked' en el OrgInvitation. El token deja de funcionar inmediatamente incluso si el correo sigue en la bandeja de entrada del destinatario. Las invitaciones de invitado a misión se revocan de la misma manera desde la página de detalle de misión.
Notas de seguridad
- Los tokens de invitación son opacos, criptográficamente aleatorios y de un solo uso.
- El
InviteTokenDocvive en una colección de Firestore separada delOrgInvitationen sí — los clientes pueden buscar un token sin poder enumerar las invitaciones existentes de una organización. - Aceptar una invitación es una escritura transaccional — la membresía se crea y la invitación se marca como
acceptedde forma atómica.