Qeet Docs

MFA & step-up

TOTP + recovery codes, email/SMS OTP factors, WebAuthn as a second factor, and step-up MFA gated by a recent-verification window.

Qeet ID ships a complete multi-factor system: TOTP with recovery codes, email/SMS OTP factors, WebAuthn as a second factor, and step-up MFA that gates sensitive actions behind a recent verification.

All implemented

TOTP, OTP factors, WebAuthn-2FA, and step-up are live. Any factor verification refreshes a recent-MFA window that a server-side RequireRecentMFA middleware checks before sensitive operations.

Factors

FactorEndpointsNotes
TOTP (RFC 6238)/v1/mfa/totp/*6-digit authenticator codes; enroll → confirm → verify.
Recovery codes/v1/mfa/recovery-codes*bcrypt-hashed single-use codes; regenerable.
Email / SMS OTP/v1/mfa/otp/factors*Per-factor challenge + confirm via real senders.
WebAuthn 2FA/v1/mfa/webauthn/*Reuses the user's registered passkeys as a second factor.

TOTP

Three-step enrollment, then verification at sign-in.

Start enrollment

Returns the shared secret + provisioning URI to render as a QR code.

POST/v1/mfa/totp/enroll/startBegin TOTP enrollment

Confirm

The user enters the first 6-digit code to prove the authenticator is synced.

POST/v1/mfa/totp/enroll/confirmConfirm enrollment

Verify at sign-in

POST/v1/mfa/totp/verifyVerify a TOTP code

Remove TOTP with DELETE /v1/mfa/totp (this is a sensitive action — see Step-up).

Recovery codes

Issued at enrollment, single-use, bcrypt-hashed. Users can regenerate the set (another step-up-gated action).

GET/v1/mfa/recovery-codesList remaining recovery codes
POST/v1/mfa/recovery-codes/regenerateRegenerate the set

Email / SMS OTP factors

Register a contact factor, then challenge + confirm it as a second step.

POST/v1/mfa/otp/factorsRegister an OTP factor (email/SMS)
POST/v1/mfa/otp/factors/{id}/challengeSend a code
POST/v1/mfa/otp/factors/{id}/confirmConfirm enrollment
POST/v1/mfa/otp/verifyVerify an OTP at sign-in

SMS is lower assurance

Prefer TOTP or WebAuthn as the primary second factor; SMS is vulnerable to SIM-swap. SMS/email delivery also depends on a configured sending domain / Twilio account.

WebAuthn as a second factor

Step-up with a registered passkey. Unlike passwordless login, this asserts the known user and issues no new token — it only proves presence to refresh the MFA window.

POST/v1/mfa/webauthn/challengeGet assertion options
POST/v1/mfa/webauthn/verifyVerify the assertion

Step-up MFA

Any successful factor verification refreshes a recent-verification window. The RequireRecentMFA middleware gates sensitive endpoints (e.g. disabling MFA, regenerating recovery codes) and returns an MFA challenge if the window has lapsed.

Check whether the user currently satisfies the window:

GET/v1/mfa/step-up/statusIs a recent MFA present?
Bash
curl https://api.qeetid.com/v1/mfa/step-up/status \
  -H "Authorization: Bearer $ACCESS_TOKEN"
# → { "recent": false }  →  prompt the user to re-verify a factor

The pattern: call step-up/status before a sensitive action; if not recent, run any factor's verify endpoint to refresh the window, then retry. The server enforces this independently — clients can't bypass it.

On this page