Authentication & RBAC

Password authentication, JWT tokens, and role-based access control.

6 min read

Dual-Mode Auth

GroundWave supports two authentication modes, selected at server start-up via the AUTH_REQUIRED environment variable. This lets operators run an open, callsign-only deployment for trusted teams while enabling full password protection for environments with broader access.

Mode AUTH_REQUIRED Behavior
Open (Phase 1) false Users register with a callsign only. No password required. All authenticated users are treated as operators.
Authenticated true Users must register with a callsign and password. JWT tokens required for all API and Socket.IO access. Roles are enforced.

Switching from open mode to authenticated mode on an existing deployment requires all existing users to set passwords. Plan the transition before going to the field.

Registration

New users register by posting to POST /api/users/register. In open mode, only a callsign is required. In authenticated mode, a password must also be supplied.

POST /api/users/register Public
Register a new user account.
Body: { "callsign": "ALPHA-1", "password": "..." }password is required when AUTH_REQUIRED=true.

The first user to register is automatically promoted to the admin role. All subsequent registrations receive the observer role by default. An admin can promote users to operator or admin via the admin dashboard.

Passwords are hashed using argon2id — the memory-hard winner of the Password Hashing Competition. Raw passwords are never stored or logged. The hash is stored in the users table alongside the salt automatically managed by the argon2 library.

Login

Registered users authenticate by posting their callsign and password. A successful login returns a signed JWT token that must be included with every subsequent request.

POST /api/auth/login Public
Authenticate and receive a JWT token.
Body: { "callsign": "ALPHA-1", "password": "..." }
Returns: { "token": "eyJ...", "user": { "id", "callsign", "role" } }

The login endpoint is rate-limited to prevent brute-force attacks. After a configurable number of failed attempts from the same IP address, further attempts are temporarily blocked and a 429 Too Many Requests response is returned.

JWT Tokens

Tokens are signed with a secret derived from the JWT_SECRET environment variable. The expiration window is configurable via JWT_EXPIRY (default: 24h).

Tokens are transmitted in two ways depending on the transport:

  • REST requests: Include the token in the Authorization: Bearer <token> HTTP header.
  • Socket.IO connections: Pass the token as the auth query parameter when connecting: io('/', { auth: { token } }).

The client monitors token expiry and automatically redirects to the login page when a token is near expiration or when the server returns a 401 Unauthorized response. Tokens are stored in localStorage and cleared on logout or expiry detection.

GroundWave does not currently implement token revocation. If a user's account is disabled, their existing JWT remains valid until it expires. For immediate revocation, set a short JWT_EXPIRY (e.g., 1h) in high-security deployments. The admin can also disable accounts, which blocks fresh logins and Socket.IO re-authentications.

Roles

GroundWave uses a three-tier role hierarchy. Roles are stored in the users table and enforced on every API request and Socket.IO event. Higher roles inherit all capabilities of lower roles.

Observer

Observers have read-only access to the operational picture. They can connect to the server, view the live map, see position markers for all users, read chat messages in channels they are members of, and view markers and overlays. They cannot produce any data:

  • Cannot send chat messages or direct messages
  • Cannot create, edit, or delete markers
  • Cannot upload or delete files
  • Cannot create or modify map overlays
  • Can listen to voice channels but cannot transmit (PTT button disabled)

The Observer role is appropriate for commanders or liaisons who need situational awareness without the ability to modify shared data.

Operator

Operators have full operational capabilities. They can use every feature of GroundWave:

  • Send and receive chat messages in channels and direct messages
  • Create, edit, and delete markers (points, lines, polygons) with optional photo attachments
  • Upload, download, and delete files
  • Create and manage map overlays (GeoJSON, GPX, georeferenced images)
  • Transmit on voice PTT channels
  • Import markers from GeoJSON files

Operators cannot access admin-only routes such as user management, data export, tile management, federation configuration, or system telemetry.

Admin

Admins have all Operator capabilities plus exclusive access to server administration:

  • View and manage all user accounts (promote, demote, disable, reset passwords)
  • Export operational data (positions, chat, markers as GeoJSON/GPX/JSON)
  • Bulk-delete data by type or date range
  • Manage tile sets (download, import, set active tileset, delete)
  • Configure and monitor server federation peers
  • View the real-time log stream and system telemetry
  • Reset the first-run setup wizard state
  • Access the detailed health endpoint (if API key protection is disabled)

Enforcement

RBAC is enforced at two layers to ensure no path bypasses authorization:

REST Middleware

Every protected route passes through an authenticate middleware that validates the JWT from the Authorization header. Role-gated routes additionally pass through a requireRole(minRole) middleware that checks the decoded token's role against the required minimum. Requests failing either check receive a 401 or 403 response with no data leaked.

Socket.IO Event Handlers

The Socket.IO server validates the token on connection. Each event handler that requires a minimum role calls checkSocketRole(socket, minRole), which fetches the user's current role fresh from the database on every check — not from the token payload. This means that if an admin demotes a user mid-session, the change takes effect immediately for all subsequent events on that connection without requiring the user to reconnect.

Role changes are live. Demoting a user from Operator to Observer takes effect on their next Socket.IO event — no disconnect required. Promoting a user similarly grants new capabilities immediately.

Open Mode Behavior

When AUTH_REQUIRED=false, the authentication middleware is bypassed entirely — no token is needed for REST or Socket.IO. All requests are treated as coming from an operator-level user identified only by their callsign. Role checks are effectively no-ops, and the admin dashboard is accessible by any connected user. This mode is intended for trusted local networks where user management overhead is undesirable.