Summary
backend/devices/src/services/devices.py:40-42 hashes the device token with bcrypt and stores it. RegenerateDeviceToken exists but does not evict an already-connected MQTT session. No RevokeDeviceToken. A stolen device token stays usable until natural expiry (never, since there is no TTL).
Changes
- New RPC
RevokeDeviceToken(device_id) — clears the stored hash, publishes a kick signal.
RegenerateDeviceToken — same kick behaviour on top of existing rotation.
- MQTT session termination via EMQX management API (
POST /api/v5/clients/:clientid/kick on all nodes — ties into infra#25 when clustered).
- Optional: add
device.token_rotated_at; emit DeviceTokenRotated event for audit.
Verification
- Device connected with token T1. Call
RegenerateDeviceToken. Connection dropped; reconnect with T1 fails, with T2 succeeds.
RevokeDeviceToken — no token works; device is offline until a new one is issued.
Summary
backend/devices/src/services/devices.py:40-42hashes the device token with bcrypt and stores it.RegenerateDeviceTokenexists but does not evict an already-connected MQTT session. NoRevokeDeviceToken. A stolen device token stays usable until natural expiry (never, since there is no TTL).Changes
RevokeDeviceToken(device_id)— clears the stored hash, publishes a kick signal.RegenerateDeviceToken— same kick behaviour on top of existing rotation.POST /api/v5/clients/:clientid/kickon all nodes — ties into infra#25 when clustered).device.token_rotated_at; emitDeviceTokenRotatedevent for audit.Verification
RegenerateDeviceToken. Connection dropped; reconnect with T1 fails, with T2 succeeds.RevokeDeviceToken— no token works; device is offline until a new one is issued.