Consumer Tier 2 — OTP
App-assisted onboarding with a manufacturer-issued one-time password and server-generated certificates via EST.
The manufacturer registers devices with the Beacon Tower registry and receives a unique OTP per device. The OTP is flashed onto the device or printed on a label. At setup time, a customer app provisions the device, and the device uses the OTP to authenticate with the onboarding service and EST server. The OTP is permanent — it is never invalidated, allowing the device to re-enroll if its certificate expires.
Use Tier 2 when the manufacturer can register devices and distribute OTPs but does not operate a full PKI. The device must be able to store keys securely after enrollment. More secure than Tier 1 because the attestation credential (OTP) is generated per-device during manufacturing, not distributed by Beacon Tower.
Prerequisites
Manufacturer:
- Register devices with the Beacon Tower registry via
POST /api/devices/register - Receive a unique OTP per device
- Flash each device with:
device_id,otp_token, onboarding service base URL, Beacon Tower Root CA certificate
Platform:
- Beacon Tower onboarding service deployed
- Beacon Tower EST server deployed and configured
- Beacon Tower CA operational
- Device Registry accessible from EST server
Device hardware:
- Network connectivity (WiFi via BLE/SoftAP provisioning)
- TLS client capable of HTTP Basic Auth
- Secure persistent storage for certificates and private keys
Sequence Diagram
Flow Walkthrough
Manufacturing
The manufacturer registers each device with the Beacon Tower registry:
curl -X POST https://<onboarding-host>/api/devices/register \
-H "Authorization: Bearer $MFG_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"device_id": "charger-x100-001",
"attestation_type": "otp",
"enrollment_group_id": "<enrollment-group-uuid>"
}'
Response:
{
"device_id": "charger-x100-001",
"otp_token": "otp_7f3a9c2e1d..."
}
The manufacturer flashes the device with the device_id, otp_token, onboarding service base URL, and Beacon Tower Root CA certificate.
Step 1: Provision via App
The customer app scans the device and calls the Provision API:
curl -X POST https://<onboarding-host>/api/provisioning \
-H "Authorization: Bearer $APP_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"device_id": "charger-x100-001",
"enrollment_group_id": "<enrollment-group-uuid>"
}'
The enrollment group defines the tenant, attestation type (OTP), provider template, and routing target. The onboarding service resolves the provider template and provisions the device with the provider.
The app provides WiFi credentials to the device over BLE or SoftAP.
Step 2: Bootstrap Discovery
The device calls the onboarding service to learn where everything is:
curl -X GET https://<onboarding-host>/get_settings \
-H "Authorization: Basic <base64(device_id:otp_token)>"
Response contains the broker URL, EST enrollment URL, and EST renewal URL. The device now knows where to enroll and where to connect.
Step 3: EST Enrollment
-
Fetch CA chain —
GET /.well-known/est/cacertsreturns the Beacon Tower CA chain. -
Enroll —
POST /.well-known/est/serverkeygenwith HTTP Basic Auth (device_id:otp_token). EST generates the keypair server-side and returns the signed certificate (PKCS#7) and private key (PKCS#8). -
OTP preserved — The OTP is validated but NOT invalidated. It remains as the device's permanent credential for future
get_settingscalls and re-enrollment.
The device stores the certificate and private key in secure persistent storage.
Step 4: Connect to Broker
The device connects to the MQTT broker (URL from Step 2) using mTLS with the operational certificate.
Step 5: Certificate Renewal
Before the certificate expires, the device calls POST /.well-known/est/serverkeygen using its current operational certificate for mTLS authentication. No OTP is needed — the valid certificate proves identity.
Step 6: Re-enrollment (Certificate Expired)
If the certificate expires before renewal (e.g., device was offline too long), the device falls back to OTP authentication: calls get_settings to refresh URLs, then re-enrolls via serverkeygen with Basic Auth. The OTP is permanent, so this always works.
Lifecycle Operations
The six steps above cover the device-driven happy path. The operations below cover the rest of the lifecycle — admin-driven (revoke, disable, approval gating) and device-driven recovery from states the happy path cannot fix on its own (session loss, shard rotation).
Session-loss recovery / shard rotation — POST /reenroll
Used when the device still holds a valid operational certificate but its MQTT session is broken — broker refused CONNECT, MQTT 5 reason 0x82 (session-taken-over) / 0x9C (use-another-server), or the device woke up after a fleet rebalance and its provider shard moved.
Distinct from Step 6 above: Step 6 is "my cert is gone, give me a new one." /reenroll is "my cert is fine, tell me where to connect."
POST /reenroll
Authorization: Basic <base64(device_id:otp_token)> # OTP path
OR
(mTLS with current operational cert) # cert path
| Status | Meaning |
|---|---|
200 | Existing routing is healthy — idempotent fast path. Device reconnects with current credentials. |
201 | Routing was refreshed (shard rotation). For cert-based devices the X.509 credentials are unchanged; rotate via Step 5 if needed. |
202 | Admin approval required — retry after the indicated interval (see below). |
403 | The enrollment group's re-enrollment policy denied the request. |
409 | Re-enroll not supported for the routing target type. |
503 | Provider unavailable — retry with backoff. |
Rate-limited per device.
Admin-gated approval
An enrollment group can be configured to require an admin decision on every re-enrollment. When that gate is active, the device's /reenroll or /.well-known/est/serverkeygen call returns 202 Accepted with a Retry-After header instead of issuing credentials. The device keeps retrying on that cadence until an admin makes a decision:
- Admin reads the parked request:
GET /api/devices/{deviceRecordId}/pending-reenrollment. - Admin approves or rejects:
POST /api/devices/{deviceRecordId}/pending-reenrollment/approve— next device retry mints the cert.POST /api/devices/{deviceRecordId}/pending-reenrollment/reject— next device retry returns403until the parked request expires (group-configured TTL).
Use this for fleets where every re-enrollment must trace back to a human decision.
Revoke a device
Two operations, both admin-only (JWT). Run them together for a full revoke:
| Endpoint | Effect |
|---|---|
DELETE /api/devices/{deviceId} | Soft-deletes the onboarding device record. Subsequent get_settings / serverkeygen / reenroll calls fail. |
POST /api/devices/{deviceId}/delete-provider-client | Tells the provider to evict the MQTT user + ACL rules. Returns 202 Accepted once queued. |
Order matters only for which call returns last; both are idempotent. After a revoke the OTP is still on file (soft delete is reversible) but the device cannot reach any device-facing endpoint.
Disable / re-enable
Soft-delete is the disable mechanism. Restore brings the device back:
| Endpoint | Effect |
|---|---|
DELETE /api/devices/{deviceId} | Soft-delete (same as revoke). Device cannot enrol, renew, or re-enrol. |
POST /api/devices/{deviceId}/restore | Undoes the soft-delete. Cert (if still valid) works again; OTP still works for get_settings and re-enrollment. |
POST /api/devices/bulk-delete / bulk-restore | Batch variants (NDJSON body). |
If you re-enable a device whose cert has expired, the device falls back to the Step 6 OTP path on its own.
Security Considerations
- Permanent OTP — The OTP is never invalidated. It serves as a permanent fallback credential for
get_settingsand re-enrollment. If the OTP is compromised, only one device is affected (per-device credential). - Per-device credential — Each device has a unique OTP, unlike Tier 1 where shared secrets are distributed per enrollment group. A compromised OTP affects only one device.
- Server-generated keys — The private key is generated by EST and transported once over TLS. After initial enrollment, renewals also generate new keys server-side.
- Certificate TTL — Determined by the enrollment group configuration. After expiry, the device re-enrolls using its permanent OTP.
- Secure storage required — The device must protect the private key after receiving it. Without secure storage, consider Tier 3 where keys never leave the device.
Next Steps
- Consumer Tier 1 — Shared Key — Simpler flow for devices without OTP capability
- Consumer Tier 3 — Birth Certificate — Highest security with on-device key generation
- B2B Tier 2 — OTP — Same attestation tier without a customer app