Mi Home cloud: avoid repeated 2FA prompts / 24h lockout for accounts … - #1683
Mi Home cloud: avoid repeated 2FA prompts / 24h lockout for accounts …#1683s1mptom wants to merge 1 commit into
Conversation
…far from the cloud server Problem ------- The Mi Home cloud protocol (DreameVacuumMiHomeCloudProtocol) uses short 6-10s HTTP/MQTT timeouts. When the Xiaomi account's cloud region is geographically far from the user (e.g. a CN-region account accessed from the EU/UK), ordinary latency regularly exceeds those timeouts. A timed-out request returns None, which is indistinguishable from a real authentication failure, so check_login()/the request helpers report the session as "not logged in" and trigger a full password re-login. That re-login needs 2FA; the emailed code can't be entered automatically, so it fails and retries on the next poll. After a few cycles Xiaomi temporarily blocks 2FA (~24h) and signs the account out everywhere - including the phone app. So a slow network, not an actual credential problem, ends up locking the user out of their own account. Solution -------- All changes are confined to DreameVacuumMiHomeCloudProtocol; the Dreame cloud and local protocols are untouched. 1. Longer timeouts + retries. Mi Home HTTP timeouts raised 6/10s -> 120s and request() retries with backoff. request() records _last_timeout (response is None) so callers can tell a true network timeout apart from a server (non-200) response. 2. Don't re-login on a timeout. check_login(), when validating the cached session and getting no response, keeps the session on a network timeout (returns True) and only treats an actual server rejection as an auth failure. This stops transient latency from cascading into a 2FA storm. 3. Silent session refresh via passToken. New refresh_token() mirrors the Mi Home app: serviceLogin with the stored passToken -> fresh ssecurity + serviceToken, no password/2FA. login() now tries check_login() -> refresh_token() -> full password+2FA login (last resort). passToken is captured from serviceLogin and carried as an optional 5th field in auth_key, so it survives restarts. Backward compatible: an existing 4-field auth_key still parses. Result: accounts far from their cloud server no longer get repeated 2FA prompts or 24h lockouts; the session refreshes silently, like the official app. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There is a reason these timeouts are so short to comply both Home Assistant and Map Parse requirements.
There is no way to know service token is valid or not if the check_login request fails so your fix causes a false positive effect, you cannot assume that your auth is still valid when the integration cannot get a valid result from check_login request.
I cannot find any reference for usage of |
|
I have made some research and realized that |
|
You're right about I reworked this as the smaller cookie-based approach you described:
I couldn't push to this contributor branch with the |
|
You should note that it is very hard for me to trigger a service token expiration on my accounts for both DE and CN servers. It should not happen in normal circumstances since server stores your last ip location and will only trigger token expiration when you try to login from a different ip location than before or using a VPN service that uses shared public ips which are not trusted by MiHome cloud. |
good to know... got it =) i think that my main problem is 5G with random IP every minute (CGNAT and so on...) =) maybe this is root cause of that problem |
…far from the cloud server
Problem
The Mi Home cloud protocol (DreameVacuumMiHomeCloudProtocol) uses short 6-10s HTTP/MQTT timeouts. When the Xiaomi account's cloud region is geographically far from the user (e.g. a CN-region account accessed from the EU/UK), ordinary latency regularly exceeds those timeouts especially for heavy requests (map i guess).
A timed-out request returns None, which is indistinguishable from a real authentication failure, so check_login()/the request helpers report the session as "not logged in" and trigger a full password re-login. That re-login needs 2FA; the emailed code can't be entered automatically, so it fails and retries on the next poll. After a few cycles Xiaomi temporarily blocks 2FA (~24h) and signs the account out everywhere - including the phone app.
So a slow network, not an actual credential problem, ends up locking the user out of their own account.
Solution
All changes are confined to DreameVacuumMiHomeCloudProtocol; the Dreame cloud and local protocols are untouched.
Longer timeouts + retries. Mi Home HTTP timeouts raised 6/10s -> 120s and request() retries with backoff. request() records _last_timeout (response is None) so callers can tell a true network timeout apart from a server (non-200) response.
Don't re-login on a timeout. check_login(), when validating the cached session and getting no response, keeps the session on a network timeout (returns True) and only treats an actual server rejection as an auth failure. This stops transient latency from cascading into a 2FA storm.
Silent session refresh via passToken. New refresh_token() mirrors the Mi Home app: serviceLogin with the stored passToken -> fresh ssecurity + serviceToken, no password/2FA. login() now tries check_login() -> refresh_token() -> full password+2FA login (last resort). passToken is captured from serviceLogin and carried as an optional 5th field in auth_key, so it survives restarts. Backward compatible: an existing 4-field auth_key still parses.
Result: accounts far from their cloud server no longer get repeated 2FA prompts or 24h lockouts; the session refreshes silently, like the official app.
All changes were made with Claude Code, so you can verify the logic and implement it yourself. The main fix is using passToken to refresh tokens. As for me, this completely fixed the login problem.