Skip to content

Invites

ARGUS has two distinct invite flows. Both use signed, single-use tokens and both land on the same /invite route, but they differ in scope and lifetime.

The two kinds of invite

Org invites — full accounts

An org invite adds a user with a real OrgRole (admin, manager, operator, observer). Accepted invites create a permanent membership at orgs/{orgId}/members/{uid}.

  • Who sends: anyone with admin.users (roles superadmin, admin, manager).
  • Lifetime: 7 days default, configurable per-invite.
  • From: Admin → Users (/admin/users).

Mission-guest invites

A short-lived link giving someone access to one operation only — no org membership, no other missions, no admin surfaces. Useful for sharing an op with an outside agency for a few hours.

  • Who sends: mission owner or org admin.
  • Lifetime: 1 h / 12 h / 24 h / 7 d.
  • From: Mission detail → Share (/operations/:id).
  • Access: read-only by default; optional “can annotate” / “can post on PTT” toggles.

Guests sign in or register for that one op; when the link expires or is revoked they lose access immediately.

Sending an org invite

  1. Go to /admin/users, click Invite user.
  2. Fill in Email, Role (built-in or custom if your plan supports it), optional expires in (default 7 days), optional message.
  3. Click Send. The server creates an OrgInvitation + InviteTokenDoc with the opaque token, then sends the email.

The pending invite appears in the users list with a grey “Pending” badge — from there you can Copy link, Resend, or Revoke.

Sending a mission-guest invite

  1. Open the mission at /operations/:id.
  2. Click Share.
  3. Pick As guest (vs. adding an existing user as a participant).
  4. Enter email, set expiry, pick a permission preset, click Send.

The mission document gets a new entry in its guestInvites subcollection and the email goes out with the same /invite?token=... URL shape — the token carries whether it’s an org invite or a mission-guest invite.

Redeeming an invite

The invite email contains a link like https://app.argus.tactical/invite?token=<opaque>. Opening it routes to InviteComponent at /invite, which:

  1. Verifies the token. States: loadingready / invalid / expired / revoked / accepted.
  2. Displays the offer — inviter, org name, role, and expiry date.
  3. Branches on auth:
    • Already signed in — shows Accept invitation. InvitationService.acceptInvite() runs the server transaction, creates the membership, and lands you on the dashboard.
    • Not signed in — mini auth form pre-filled with the invite email. Toggle between Sign in and Create account; submitting authenticates and accepts in one step (loginAndAccept() / registerAndAccept()).
  4. The “Welcome to {org}” state renders with an Enter Dashboard button.

What can go wrong

  • Invalid — token doesn’t exist / is malformed.
  • Expired — past expiresOn.
  • Revoked — sender revoked it before you accepted.
  • Already accepted — you’ve used it before.
  • Error — server-side failure during acceptInvite; the component offers Try again.

The email on the invite is authoritative — accepting while signed in as a different user is rejected.

Revoking an invite

From /admin/usersPending invites → row actions → Revoke. Revoking writes status: 'revoked' on the OrgInvitation. The token immediately stops working even if the email is still in the recipient’s inbox. Mission-guest invites revoke the same way from the mission detail page.

Security notes

  • Invite tokens are opaque, cryptographically random, and single-use.
  • The InviteTokenDoc lives in a separate Firestore collection from the OrgInvitation itself — clients can look up a token without being able to enumerate existing invitations for an org.
  • Accepting an invite is a transactional write — the membership is created and the invitation is marked accepted atomically.