Skip to content

fix: device flow client_id, form-encoded auth body, expired token fallback#314

Open
manW13-be wants to merge 2 commits into
wmalgadey:masterfrom
manW13-be:fix/device-flow-client-id-token-refresh
Open

fix: device flow client_id, form-encoded auth body, expired token fallback#314
manW13-be wants to merge 2 commits into
wmalgadey:masterfrom
manW13-be:fix/device-flow-client-id-token-refresh

Conversation

@manW13-be
Copy link
Copy Markdown

Summary

Four bugs affecting the OAuth2 device flow and token refresh, confirmed against login.tado.com in production.

Bug 1 — Verification URL missing client_id

device_verification_url() returned a URL with only user_code. Opening it in a browser causes login.tado.com to reject the session with missing_client_id. Fixed: both user_code and client_id are now included.

Bug 2 — NOT_STARTED when saved token is expired

When a token file existed but the refresh failed (expired token), there was no fallback — status stayed NOT_STARTED indefinitely. Fixed: added the missing else branch to start a new device flow in that case.

Bug 3 — client_id as constructor parameter

The only way to pass a custom client_id was to monkey-patch PyTado.const.CLIENT_ID_DEVICE, a module-level global shared across all instances. Fixed: Http.__init__ and Tado.__init__ now accept client_id=None, defaulting to CLIENT_ID_DEVICE if not provided.

Bug 4 — Auth requests sent as query string + empty JSON body

_refresh_token, _login_device_flow, and device polling sent parameters via params= (query string) with data=json.dumps({}) and Content-Type: application/json. The Tado auth server requires application/x-www-form-urlencoded. Fixed on all three call sites.

Test plan

  • Device flow with a fresh token file: verification URL contains both user_code and client_id
  • Device flow with an expired token file: falls back to a new device flow instead of staying NOT_STARTED
  • Tado(client_id="custom-id") works without patching PyTado.const
  • Token refresh succeeds (form-encoded body accepted by login.tado.com)

…lback

- Add `client_id` parameter to `Http.__init__` and `Tado.__init__`,
  defaulting to `CLIENT_ID_DEVICE`. Removes the need to monkey-patch
  the module global for custom client IDs.

- Fix verification URL to include `client_id` query param (required by
  login.tado.com — omitting it causes `missing_client_id` rejection).

- Fix `_refresh_token`, `_login_device_flow`, and device polling to use
  form-encoded body (`application/x-www-form-urlencoded`) instead of
  query params + empty JSON body. The Tado auth server requires a form
  body; the previous format silently failed.

- Add missing `else` branch in `__init__`: when a saved token exists but
  the refresh fails (expired), fall back to a new device flow instead of
  staying in `NOT_STARTED` indefinitely.
@manW13-be manW13-be force-pushed the fix/device-flow-client-id-token-refresh branch from 7b55319 to 34ae9cf Compare May 2, 2026 06:47
@wmalgadey
Copy link
Copy Markdown
Owner

@manW13-be willing to fix the CI/Lint and tests?

Token refresh now sends params as form-encoded body, not query string.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants