medusajs/medusa · error · MedusaError

MFA verification code is required to disable MFA

Error message

MFA verification code is required to disable MFA

What it means

When the MFA disable policy is 'challenge', disabling an enabled factor requires both a method and a fresh verification code. Omitting either throws INVALID_DATA before verification is attempted.

Source

Thrown at packages/modules/auth/src/services/auth-module.ts:534

  }

  @InjectManager()
  async disableAuthMfa(
    data: AuthTypes.DisableAuthMfaDTO,
    @MedusaContext() sharedContext: Context = {}
  ): Promise<AuthTypes.AuthMfaDTO> {
    const factor = await this.authMfaFactorService_.retrieve(
      data.id,
      {},
      sharedContext
    )

    if (
      factor.status === "enabled" &&
      this.getMfaDisablePolicy_() === "challenge"
    ) {
      if (!data.method || !data.code) {
        throw new MedusaError(
          MedusaError.Types.INVALID_DATA,
          "MFA verification code is required to disable MFA"
        )
      }

      const valid = await this.authMfaProviderService_.verify(
        data.method,
        {
          auth_identity_id: factor.auth_identity_id,
          code: data.code,
        },
        sharedContext
      )

      if (!valid) {
        throw new MedusaError(
          MedusaError.Types.NOT_ALLOWED,
          "Invalid MFA verification code"

View on GitHub (pinned to 5e06e544a2)

Solutions

  1. Collect the current MFA code and pass method (e.g. 'totp') plus code in the disable payload
  2. If appropriate for your deployment, change the MFA disable policy configuration away from 'challenge'

Example fix

// before
await authModuleService.disableAuthMfa({ id: factorId })
// after
await authModuleService.disableAuthMfa({
  id: factorId,
  method: 'totp',
  code: currentCode,
})
Defensive patterns

Strategy: validation

Validate before calling

if (policy === 'challenge' && factor.status === 'enabled' && (!method || !code)) {
  throw new Error('collect MFA code before disabling')
}

Type guard

const needsChallenge = (factor: { status: string }) => factor.status === 'enabled' && policy === 'challenge'

Prevention

When it happens

Trigger: disableAuthMfa({ id }) without method/code while factor.status === 'enabled' and the configured disable policy is 'challenge'.

Common situations: Admin UI 'remove MFA' button that doesn't prompt for a code; policy switched to 'challenge' after the UI was built; API consumers unaware of the policy config.

Understand the failure class

Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.

Related errors


AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27). Data as JSON: /api/errors/2b3c6a173c6dc42f. Report an issue: GitHub.