private@homelab: ~/latest
local-first guides · privacy-aware · no noisy tracking
private@homelab:~$ cat guides/article.md
· ·
6–10 minutes
read

Xiaomi Cloud Tokens Extractor Stuck on 2FA? Here’s the Fix

Xiaomi Cloud Tokens Extractor stuck in a 2FA login loop? Here's the update that clears it, the official checklist, and the DevTools workaround.

Xiaomi Cloud Tokens Extractor Stuck on 2FA? Here’s the Fix

If you own Xiaomi or Mijia gear that doesn’t speak native Zigbee — a Wi-Fi plug, a BLE sensor, a robot vacuum, a non-Aqara gateway — you eventually run into the same wall. To get the local device token that python-miio, hass-xiaomi-miot, or Xiaomi Cloud Map Extractor need for local control, you have to run PiotrMachowski’s Xiaomi-cloud-tokens-extractor against your Mi account once. It’s a one-time step, or an occasional one if you re-pair a device. It is not something that runs continuously once your local integration is working.

For a lot of people that one-time step is where the whole project stalls. The script asks for a 2FA verification, you confirm it in the browser, and the script just asks again. And again.

Why You Need a Cloud Login for a “Local” Setup

This trips people up before they’ve even hit the 2FA bug. If the whole point is local control, why does it need your Xiaomi cloud credentials at all?

Because the token itself lives behind Xiaomi’s account system, not on the device. The extractor logs into your Mi account, pulls the local token (and, for BLE devices, the encryption key) for each device tied to it, and then hands you that credential to use locally from then on. Once you’ve saved the token, day-to-day control through python-miio or hass-xiaomi-miot doesn’t need an active cloud session. The cloud login is the key-fetching step, not the operating mode.

The 2FA Loop: What It Looks Like and Why It Happens

The pattern shows up the same way across a cluster of GitHub issues on both the token extractor itself (issues #42, #43, #141) and the hass-xiaomi-miot integration (issues #2446, #2635), plus a long, mostly-unresolved Home Assistant Community thread. The sibling Xiaomi Cloud Map Extractor project has the same failure mode reported against it too, repeatedly, over multiple years (issues #157, #319, #471), which is a good sign this is a standing bug in the shared login code rather than something specific to one tool. You enter your username and password, the tool prints a 2FA verification URL, you open it and get a confirmation that everything checked out. Then you restart the extractor and it prints the same URL again, expecting you to confirm something it already has confirmation for.

The root cause is a login-flow gap. The script’s re-login check depended on a signal the browser confirmation never actually sent back to it, so it kept treating the account as unverified no matter how many times you completed the challenge in a browser tab. It’s a logic bug in how the script tracks verification state, not a problem with your account or your password.

Update Before You Try Anything Else

Before doing anything more involved, check whether your installed copy is current. The loop bites hardest on older clones and pinned versions.

Recent tagged releases have folded in fixes for this. Release v1.4.1 bundles a set of community-contributed two-factor improvements, and separately, pull request #142 rewrote the 2FA flow so the verification code is submitted directly to the script instead of only being confirmed in a browser, and added session-file persistence so you’re not re-authenticating on every run. The maintainer closed that PR once the functionality landed.

If your git clone or download predates those changes, pulling the latest release is the fastest first step, faster than the manual workaround further down this page. Whether the exact variant of the loop you’re hitting is fully resolved in the newest version can depend on your account’s region and 2FA method, so confirm you’re on an up-to-date copy before assuming you still need the DevTools patch.

The Official Troubleshooting Checklist

If updating doesn’t clear it, or you’re stuck on an older pinned version for another reason, the project’s own README has a short troubleshooting list worth running through first.

  • Check your spam folder. The 2FA email doesn’t always land where you expect.
  • Switch to QR-code login instead of username and password if the loop persists. It uses a different auth path.
  • Watch the rate limit. Xiaomi caps you at somewhere around 3 to 5 2FA requests per day, depending on region, and hammering the login repeatedly just burns through that budget without fixing anything.
  • Disable network-level ad blockers and DNS filtering. AdGuard, Pi-hole, and Unifi country-restriction rules have all been reported to silently break the auth handshake without throwing an obvious error.

That checklist resolves it for a meaningful share of people. It doesn’t resolve it for everyone, which is where the manual route comes in.

The Manual Fallback: Patching Session Values Yourself

This is your own Mi account, your own devices, and a documented community workaround for getting local control working, not anything involving anyone else’s credentials. Two independent Chinese-language sources, a Tencent Cloud developer-community 踩坑 (literally “stepping in a pit,” developer slang for a hands-on troubleshooting writeup) post and a CSDN article, converged on the identical fix without apparently knowing about each other. That kind of independent convergence is a decent signal that the workaround is real and repeatable, not a one-off hack that happened to work for one person.

The idea is to skip the script’s broken re-login check entirely by handing it the session values it would have gotten from a successful login, pulled straight out of your browser instead. Here’s what that looks like in practice.

  1. Run the extractor as normal until it prints the 2FA verification URL.
  2. Open that URL in a desktop browser with DevTools open (F12, then the Network tab), and complete the 2FA challenge.
  3. With the Network tab still capturing requests, search the captured requests and responses for four specific values: sSecurity, userid, servicetoken, and cUserId. They appear in the login and verification exchanges that follow the 2FA confirmation. Use the DevTools filter box and search each name in turn rather than assuming they all sit in a single response.
  4. Open token_extractor.py and hardcode those four values in place of wherever the script currently fetches them.
  5. Find the line logged = connector.login() and change it to logged = True, so the script stops trying to re-verify a login it already has valid session data for.
  6. Re-run the extractor. It should proceed straight to pulling tokens instead of asking for 2FA again.

Worth knowing: those four session values are functionally as sensitive as your account password while they’re valid. Don’t paste them into a GitHub issue, a Discord message, or a public gist when asking for help, and don’t leave token_extractor.py with them hardcoded in a location anyone else can read. Treat the patched file the same way you’d treat any other file holding a live credential.

A Self-Hosted Alternative: xiaomi-tokens-web

If patching Python by hand isn’t your idea of a good afternoon, rankjie/xiaomi-tokens-web, built by the same contributor behind PR #142, reimplements the whole extraction flow as a self-hostable TypeScript/Hono.js web app with working 2FA support. You enter the SMS or email code directly in its UI instead of confirming in a separate browser tab, which is exactly the loop this whole article is about avoiding.

One caveat matters more here than it would for most tools. The project’s own README warns that its public hosted demo instance routes your Xiaomi account credentials through a third-party server, and explicitly says that’s not recommended for privacy-sensitive users. Self-hosting the container yourself is the documented recommendation, not an optional extra. That’s the right call if you’re already running a few self-hosted services and don’t mind one more container, but if this is a once-off job, patching the session values by hand costs nothing extra and doesn’t require standing up new infrastructure.

What to Do With the Token Once You Have It

Once you’re through the login, the extractor’s output is a list of devices with their IPs, tokens, and (for BLE devices) encryption keys. Where that goes next depends on the device. Most Wi-Fi Xiaomi/Mijia gear goes through hass-xiaomi-miot. If you’re scripting something custom, python-miio directly is the more flexible option. And if it’s a robot vacuum you’re after, the Xiaomi Cloud Map Extractor is the one built specifically for pulling map data. If your device is a BLE sensor rather than a Wi-Fi one, you may not need this tool at all — BLE sensors like the LYWSD03MMC broadcast their readings locally with no token involved. And if what actually brought you here was a gateway rather than a standalone device, the multi-mode gateway setup runs into a related, but not identical, cloud dependency.

Once the token’s saved, you shouldn’t need to touch this login flow again unless a device gets re-paired or reset. That’s also roughly the same one-time-cloud-touch pattern this cluster keeps running into elsewhere, worth keeping in mind if you’re also weighing whether to delete a Xiaomi account entirely.

The underlying 2FA bug has been reported, in one form or another, across three different repos for a couple of years. The maintained releases have gradually absorbed real fixes for it rather than leaving everyone on the manual workaround, so the single most useful habit is to start from an up-to-date copy — check your version before you reach for the DevTools patch out of reflex.

local-firstHome Assistantno-cloud