Auto-reset device_token_ack when a reprovisioned frame is reclaimed
Build and push server image / test (push) Successful in 21s
Build and push server image / build-and-push (push) Successful in 1m58s
Build and push server image / deploy (push) Successful in 53s

Once set, device_token_ack permanently locks out id-only requests
(auth.require_device) -- fine for a device that still has its token,
but a reprovisioned device has wiped its own token locally and had no
way back in short of a manual DB edit. The device's captive portal
always redirects the phone to /claim?device_id=... after
(re)provisioning, so reopen the handshake window there instead,
scoped to a logged-in owner/linked user of that frame.
This commit is contained in:
2026-07-24 15:30:45 -04:00
parent 569bf733e9
commit 82f60ed428
2 changed files with 99 additions and 0 deletions
+17
View File
@@ -310,6 +310,23 @@ def _render_claim(request: Request, db: Session, device_id: str, error: str | No
frame.owner_user_id == user.id or db.get(UserFrame, (user.id, frame.id)) is not None
):
status, pending_yours = "claimed_yours", False
if frame.device_token_ack:
# The device's captive portal redirects here on EVERY
# (re)provisioning cycle (see wifi_provisioning.c) -- if the
# physical frame was reset/reprovisioned, it no longer has
# the access token this frame row already acknowledged, and
# auth.require_device permanently locks out an id-only
# request once device_token_ack is set (device_id alone,
# unlike the token, isn't secret -- it's shown on the
# frame's own screen/QR). Reopening that handshake window
# here is what "give it a minute to connect" below actually
# depends on: it's safe because landing on this branch
# already requires knowing the device_id (physical/local
# access to the frame) AND being logged in as an owner/
# linked user of it.
frame.device_token_ack = False
db.commit()
logger.info("Frame #%d's device token handshake reopened (re-provisioned)", frame.id)
else:
status, pending_yours = "claimed", False
return templates.TemplateResponse(