Authentication & Security

RosFit enforces authentication and authorization at every layer — devices authenticate to the MQTT broker, users authenticate to the API, and role-based access control governs what each identity can do. All traffic is encrypted with TLS in production.

Security model

The diagram below shows how authentication flows through the system.

┌─────────────────────────────────────────────────────────────────────┐
│                          ROBOT SIDE                                 │
│                                                                     │
│   ┌───────────────┐         JWT / mTLS          ┌───────────────┐  │
│   │  RosFit       │ ──────────────────────────▶  │               │  │
│   │  Bridge       │                              │               │  │
│   │               │ ◀──────────────────────────  │               │  │
│   └───────────────┘     MQTT over TLS (8883)     │               │  │
│                                                  │   EMQX 5.x   │  │
└──────────────────────────────────────────────────│   Broker      │──┘
                                                   │               │
┌──────────────────────────────────────────────────│               │──┐
│                          CLOUD SIDE              │               │  │
│                                                  └───────┬───────┘  │
│                                                          │          │
│   ┌───────────────┐        JWT (Bearer)          ┌───────┴───────┐  │
│   │   React       │ ──────────────────────────▶  │   FastAPI     │  │
│   │   Dashboard   │                              │   Backend     │  │
│   │               │ ◀──────────────────────────  │               │  │
│   └───────────────┘       HTTPS (443)            └───────────────┘  │
│                                                                     │
└─────────────────────────────────────────────────────────────────────┘

There are two separate authentication planes:

  • Device plane — The RosFit Bridge authenticates to EMQX using a JWT token (or X.509 client certificate for mTLS). EMQX validates the token and enforces topic-level ACLs.
  • User plane — Dashboard users and API consumers authenticate to FastAPI using JWT bearer tokens obtained via email + password login.

Device authentication

JWT tokens

When a device is registered via POST /api/v1/devices, the API returns an access_token. This token is a signed JWT scoped to the device's MQTT topic namespace.

{
  "sub": "bot-01",
  "scope": "device",
  "topics": {
    "publish": ["rosfit/bot-01/#"],
    "subscribe": ["rosfit/bot-01/#"]
  },
  "exp": 1743350400
}

The bridge passes this token as the MQTT password field when connecting to EMQX. EMQX validates the signature against the shared secret (or public key in asymmetric mode) and grants access only to the topics listed in the token's topics claim.

X.509 mTLS certificates

For higher-security deployments, RosFit supports mutual TLS (mTLS) with X.509 client certificates.

  1. Generate a device certificate signed by your CA (or use the built-in rosfit cert generate CLI command).
  2. Configure the bridge with the certificate and private key instead of a JWT token.
  3. EMQX extracts the device ID from the certificate's Common Name (CN) and applies ACLs accordingly.

mTLS is recommended for production fleets where token rotation is impractical or where compliance requires certificate-based identity.

User authentication

Dashboard users and API consumers authenticate via email and password. The API issues a short-lived access token and a long-lived refresh token.

# Register a new user
curl -X POST http://localhost:8000/api/v1/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "password": "secure-password-here",
    "name": "Jane Operator",
    "role": "operator"
  }'

# Log in
curl -X POST http://localhost:8000/api/v1/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "[email protected]",
    "password": "secure-password-here"
  }'

Login response:

{
  "access_token": "eyJhbGciOiJIUzI1NiIs...",
  "refresh_token": "eyJhbGciOiJIUzI1NiIs...",
  "token_type": "bearer",
  "expires_in": 3600
}

The access token expires in 1 hour. Use the refresh endpoint to obtain a new one without re-authenticating:

curl -X POST http://localhost:8000/api/v1/auth/refresh \
  -H "Content-Type: application/json" \
  -d '{ "refresh_token": "eyJhbGciOiJIUzI1NiIs..." }'

Role-based access control

Every user is assigned a role that determines their permissions across the platform.

RoleDashboard accessCommandsShadowDevicesUsersOTA
adminFullSend / cancelRead / writeCreate / edit / deleteManageTrigger / rollback
operatorFullSend / cancelRead / writeViewTrigger
viewerRead-onlyRead onlyView

Permission details

  • admin — Full access to all resources. Can create and delete users, manage device credentials, configure EMQX ACLs, and trigger OTA rollouts.
  • operator — Can send commands to devices, update device shadow state, and trigger OTA updates. Cannot manage users or delete devices.
  • viewer — Read-only access to the dashboard, telemetry, and device metadata. Cannot send commands or modify any state.

Roles are assigned during user registration and can be changed by an admin via PATCH /api/v1/users/{user_id}.

MQTT topic ACLs

EMQX enforces topic-level access control lists (ACLs) so that each device can only publish and subscribe to its own namespace.

IdentityPublishSubscribe
Device bot-01rosfit/bot-01/#rosfit/bot-01/#
Device bot-02rosfit/bot-02/#rosfit/bot-02/#
Cloud services (handler, API)rosfit/+/#, rosfit/$broadcast/#, rosfit/$group/#rosfit/#

How it works

  1. When a device connects, EMQX extracts the device_id from the JWT sub claim (or X.509 CN).
  2. The built-in EMQX authorization plugin matches the device ID against the ACL rule rosfit/{device_id}/#.
  3. Any publish or subscribe attempt outside the device's namespace is rejected with an MQTT CONNACK or SUBACK error code.

This prevents a compromised device from reading telemetry of other devices or injecting commands into another device's topic namespace.

API endpoints

MethodEndpointDescriptionAuth
POST/api/v1/auth/registerRegister a new user accountPublic (or admin-only in locked mode)
POST/api/v1/auth/loginAuthenticate and receive JWT tokensPublic
POST/api/v1/auth/refreshExchange a refresh token for a new access tokenRefresh token
POST/api/v1/auth/devices/tokenGenerate or rotate a device MQTT tokenAdmin / Operator
GET/api/v1/auth/meGet the current user's profile and roleBearer token
PATCH/api/v1/users/{user_id}Update user role or profile (admin only)Admin

TLS configuration

All production deployments should encrypt traffic with TLS.

ServicePortProtocolCertificate
EMQX MQTT8883MQTT over TLS 1.3Server cert (+ client cert for mTLS)
EMQX WebSocket8084WSSServer cert
FastAPI443HTTPSServer cert (via reverse proxy)
Dashboard443HTTPSServer cert (via reverse proxy)
MinIO9000HTTPS (optional)Server cert

Generating certificates

RosFit includes a CLI helper for generating a self-signed CA and device certificates for development:

# Generate a CA certificate
rosfit cert ca --out ./certs

# Generate a device certificate signed by the CA
rosfit cert device --ca ./certs/ca.pem --key ./certs/ca-key.pem \
  --device-id bot-01 --out ./certs/bot-01

# Files created:
# ./certs/bot-01/client.pem    (device certificate)
# ./certs/bot-01/client-key.pem (device private key)

For production, use certificates issued by a trusted CA or your organization's internal PKI.

EMQX TLS configuration

Configure TLS listeners in the EMQX configuration or via environment variables in Docker Compose:

# docker-compose.yml (excerpt)
emqx:
  environment:
    EMQX_LISTENER__SSL__EXTERNAL: "8883"
    EMQX_LISTENER__SSL__EXTERNAL__SSL_OPTIONS__CERTFILE: "/certs/server.pem"
    EMQX_LISTENER__SSL__EXTERNAL__SSL_OPTIONS__KEYFILE: "/certs/server-key.pem"
    EMQX_LISTENER__SSL__EXTERNAL__SSL_OPTIONS__CACERTFILE: "/certs/ca.pem"
    EMQX_LISTENER__SSL__EXTERNAL__SSL_OPTIONS__VERIFY: "verify_peer"
  volumes:
    - ./certs:/certs:ro