# invalid_code

The second-factor or recovery code was wrong or already used (401).

## Meaning

A TOTP code, an emailed one-time code or a recovery code did not verify at a hosted
UI MFA endpoint (`/v1/auth/mfa/*`): it was wrong, outside the acceptance window, or
a code already used once. The answer is the same for each, so it does not reveal
which.

## Impact

The user stays on the second-factor step. An occasional one is expected: a mistyped
code or one that expired while being typed. Repeated failures lock that method after
`mfa.max_failures` (5) for `mfa.lock_duration` (15m), and further attempts answer
`too_many_requests`.

## Diagnosis

1. Look the request id up on the Troubleshooting page. The attempt is recorded on
   the verification action (for example `mfa.totp.verified`) with reason
   `invalid_code` and a cause such as `code_mismatch`, `code_replayed`,
   `code_malformed` or `clock_skew`.
2. Many `clock_skew` causes for one user point at the phone's clock; for everyone,
   at the server's.

## Mitigation

- As a user: wait for the next code in the authenticator app and enter it once;
  check the phone's time is set automatically. If the device is gone, use a
  recovery code.
- As an operator: keep server clocks synchronised. `mfa.drift` (0, 1 or 2 steps)
  widens the window, at a cost in security.

## Escalation

Escalate when correct codes fail for many users at once. Check NTP on the replicas
first. Open an issue with request ids, the audit causes and the server version.
Never attach codes, recovery codes or TOTP seeds.

---

[Problem code runbooks](https://www.nauthera.io/docs/v1/runbooks/problems) · Generated from [`internal/runbook/problems.yaml`](https://github.com/nauthera/nauthera-server/blob/main/internal/runbook/problems.yaml) in nauthera-server.
