Skip to content
Back

MFA TOTP State Inconsistency After Disable/Re-enable Flow — Invalid Token During Verification

  • 0
  • Auth
Brightgoal
8 May, 2026, 03:47

MFA TOTP State Inconsistency After Disable/Re-enable Flow — Invalid Token During Verification

Environment:

  • Next.js
  • node-appwrite
  • Session-based authentication
  • TOTP MFA

Initially, my MFA disable flow was:

TypeScript
await account.updateMFA({ mfa: false });

await account.deleteMFAAuthenticator({
    type: AuthenticatorType.Totp,
});

After running this flow, listMFAFactors() still returned:

TypeScript
{
  "totp": true,
  "phone": false,
  "email": true,
  "recoveryCode": true
}

Observed behavior:

  • UI still showed MFA enabled
  • Recovery codes remained available
  • MFA appeared disabled internally but factor state still remained active

Then I changed the disable flow order to:

TypeScript
await account.deleteMFAAuthenticator({
    type: AuthenticatorType.Totp,
});

await account.updateMFA({ mfa: false });

After this change, listMFAFactors() correctly returned:

TypeScript
{
  "totp": false,
  "phone": false,
  "email": true,
  "recoveryCode": true
}

and the UI correctly showed MFA disabled.

However, after this flow change, MFA could no longer be re-enabled successfully.

My enable flow is:

  1. createMFAAuthenticator()
  2. Scan QR code
  3. Enter OTP
  4. updateMFAAuthenticator()
  5. createMFARecoveryCodes()
  6. updateMFA({ mfa: true })

The failure happens exactly at:

TypeScript
await account.updateMFAAuthenticator({
    type: AuthenticatorType.Totp,
    otp,
});

with the error:

TypeScript
Invalid token passed in the request.

Important details:

  • QR code is freshly generated
  • OTP is valid
  • OTP entered within ~10 seconds
  • Session is valid
  • Authenticator app is synced correctly
  • Error started only after changing the disable flow order

Before changing the flow order:

  • MFA could still be re-enabled
  • but factor state remained inconsistent (totp: true)

After changing the flow order:

  • factor state became correct (totp: false)
  • but OTP verification permanently fails
TL;DR
User encountered an issue with MFA TOTP, where enable → disable → re-enable flow works for new accounts but not for older accounts. The older account seems to be stuck in an inconsistent MFA state, resulting in an invalid token error during verification. The user doesn't need a fix for the old account and can delete it but wanted to flag the issue for previously affected accounts. Solution: It was identified as a bug, and a fix was provided in the GitHub link shared: https://github.com/appwrite/appwrite/pull/12485.
Aditya Oberai
8 May, 2026, 11:05

<@287294735054274560> can you take a look at this please?

Brightgoal
8 May, 2026, 13:35

<@287294735054274560> Hi, thank you for taking the time. Do you have any updates about this?

Brightgoal
9 May, 2026, 11:10

<@287294735054274560> how can we fix this ?

Brightgoal
11 May, 2026, 02:22

<@501784879638249472> Hi, good morning 😌
Could you please ask <@287294735054274560> to help me with that when he gets a chance?

Meldiron
11 May, 2026, 15:31

Heyy 👋 Checking with Torsten to get most accurate information here.

In the meantime, it seems I utilized your 2nd approach in one of my projects: https://github.com/Meldiron/almost-moments/blob/b9401804f38c9e5549c93a5ec5a832e6302b53a8/app/%28dashboard%29/dashboard/settings/page.tsx#L333-L334 Curious, what exactly is the error given when re-enabling it again in this flow?

Meldiron
11 May, 2026, 15:31

<@1472201241130958971>

Brightgoal
12 May, 2026, 14:13

<@287294735054274560> I checked out the code — yeah, it’s actually the exact same approach you used 😄 Funny coincidence, the project you shared is something I had already built around 4–5 months ago.

The concept is basically the same. The only difference is that my version doesn’t have login functionality, but apart from that, almost everything is pretty similar.

The bug I’m facing is in a different project though. Let me share the exact error I’m getting.

Brightgoal
12 May, 2026, 14:14

<@287294735054274560> "Invalid token passed in the request."

Brightgoal
12 May, 2026, 14:19

<@287294735054274560> This is the exact error I’m getting. You can check the image I shared it shows the error in more detail.

Meldiron
12 May, 2026, 14:35

Thanks for sharing 🙌 Sadly my current tasks board wont allow me to look into this for another few days. <@235411231190679552> , do you think you would have time tomorrow to reproduce those MFA issues and see if there is a bug, or if a flow/docs can be improved?

Brightgoal
12 May, 2026, 15:16

<@287294735054274560> No worries at all 🙂 I can wait a few days, and whenever you have some free time, we can look into it together. Meanwhile, I’ll keep working on other things on my side, and I’ll also try looking into this once again from my end to see if I can debug it or figure out what’s causing the issue.

Torsten Dittmann
14 May, 2026, 10:18

looking into it now 🙂

Brightgoal
6 Jun, 2026, 12:43

<@235411231190679552> Hi , Where you able to find what's the problem ?

Brightgoal
6 Jun, 2026, 12:48

<@287294735054274560> Hi Matej, Could you please help me with that ?

Brightgoal
6 Jun, 2026, 13:25

<@287294735054274560> <@501784879638249472> <@235411231190679552>

Quick update on this MFA issue I did a thorough round of testing and wanted to share what I found. On a brand-new account, MFA now works perfectly. I ran the full cycle multiple times:

Enable MFA → scan QR → enter code → got recovery codes ✅ Logged out, logged back in, passed the TOTP challenge ✅ Disabled MFA → re-enabled it → entered code → enabled successfully ✅ Logged out/in again with the challenge, disabled, logged out, logged back in without MFA, then enabled again — all worked ✅ So the enable → disable → re-enable flow is fully working for new accounts.

But it's still broken on my older account (one that had been through several enable/disable cycles previously). On that account:

listMfaFactors returns totp: false, recoveryCode: true createMfaAuthenticator('totp') succeeds and returns a secret updateMfaAuthenticator then rejects every code with 401 user_invalid_token

Brightgoal
6 Jun, 2026, 13:25

To be 100% sure it wasn't my app or my phone, I generated the TOTP code server-side, directly from the exact secret the API returned, on an NTP-synced clock (standard HMAC-SHA1, 6 digits, 30s) — and submitted it well inside the time window. Appwrite still rejected it as an invalid token. The secret was byte-identical across the API response, the otpauth URI, and what I displayed, and createMfaAuthenticator only ran once, so there's no mismatch on my side.

So it looks like the old account is stuck in an inconsistent MFA state (possibly orphaned/duplicate authenticator records where verify validates against a different secret than create returns), while fresh accounts are totally fine.

I don't need a fix for the old account specifically — those are all just test accounts and I can delete them. I mainly wanted to flag that the original issue is resolved for new accounts, but the inconsistent state still lingers on accounts that hit the bug before. Might be worth checking whether previously-affected accounts can self-heal, in case any real users got into this state.

Thanks for looking into it! 🙏

Torsten Dittmann
9 Jun, 2026, 09:15

thank you for the insights! I have an agent running in the background right now looking into it 🙏

Torsten Dittmann
9 Jun, 2026, 11:42
Reply

Reply to this thread by joining our Discord

Reply on Discord

Need support?

Join our Discord

Get community support by joining our Discord server.

Join Discord

Get premium support

Join Appwrite Pro and get email support from our team.

Learn more