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.
- Generate a device certificate signed by your CA (or use the built-in
rosfit cert generateCLI command). - Configure the bridge with the certificate and private key instead of a JWT token.
- 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.
| Role | Dashboard access | Commands | Shadow | Devices | Users | OTA |
|---|---|---|---|---|---|---|
| admin | Full | Send / cancel | Read / write | Create / edit / delete | Manage | Trigger / rollback |
| operator | Full | Send / cancel | Read / write | View | — | Trigger |
| viewer | Read-only | — | Read only | View | — | — |
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.
| Identity | Publish | Subscribe |
|---|---|---|
Device bot-01 | rosfit/bot-01/# | rosfit/bot-01/# |
Device bot-02 | rosfit/bot-02/# | rosfit/bot-02/# |
| Cloud services (handler, API) | rosfit/+/#, rosfit/$broadcast/#, rosfit/$group/# | rosfit/# |
How it works
- When a device connects, EMQX extracts the
device_idfrom the JWTsubclaim (or X.509 CN). - The built-in EMQX authorization plugin matches the device ID against the ACL rule
rosfit/{device_id}/#. - Any publish or subscribe attempt outside the device's namespace is rejected with an MQTT
CONNACKorSUBACKerror code.
This prevents a compromised device from reading telemetry of other devices or injecting commands into another device's topic namespace.
API endpoints
| Method | Endpoint | Description | Auth |
|---|---|---|---|
POST | /api/v1/auth/register | Register a new user account | Public (or admin-only in locked mode) |
POST | /api/v1/auth/login | Authenticate and receive JWT tokens | Public |
POST | /api/v1/auth/refresh | Exchange a refresh token for a new access token | Refresh token |
POST | /api/v1/auth/devices/token | Generate or rotate a device MQTT token | Admin / Operator |
GET | /api/v1/auth/me | Get the current user's profile and role | Bearer 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.
| Service | Port | Protocol | Certificate |
|---|---|---|---|
| EMQX MQTT | 8883 | MQTT over TLS 1.3 | Server cert (+ client cert for mTLS) |
| EMQX WebSocket | 8084 | WSS | Server cert |
| FastAPI | 443 | HTTPS | Server cert (via reverse proxy) |
| Dashboard | 443 | HTTPS | Server cert (via reverse proxy) |
| MinIO | 9000 | HTTPS (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