Multi-Factor Authentication
This guide covers everything you need to implement MFA in your application: how TOTP works, setting up MFA for users, handling the verification flow during login, enforcing MFA per-tenant, admin reset, and best practices.
How TOTP Works
Vyntech Account uses TOTP (Time-based One-Time Password) as defined in RFC 6238. Here's the flow at a high level:
- 1The server generates a random secret key (160-bit) and shares it with the user's authenticator app via a QR code or manual entry.
- 2Both the server and the app independently derive a 6-digit code from the shared secret + current time (30-second windows).
- 3At login, the user enters the code from their app. The server checks it against the expected value (with ±1 step tolerance for clock drift).
Compatible apps: Google Authenticator, Authy, 1Password, Bitwarden, Microsoft Authenticator, and any RFC 6238-compliant TOTP app.
Step 1: Initiate MFA Setup
When a user wants to enable MFA, call the setup endpoint. It returns a TOTP secret and a provisioning URI that you render as a QR code.
/api/v1/users/me/mfa/setup🔒 AuthInitiate MFA setup — returns secret and QR code URI.
Rendering the QR Code
You have two options for displaying the QR code to the user:
- •Use the returned SVG — the
qr_code_svgfield contains a ready-to-render SVG string. Just inject it into your DOM. - •Generate client-side — use the
provisioning_uriwith a library likeqrcode.reactorqrcodeto render the QR yourself.
Always show the secret as plain text below the QR code for users who prefer manual entry (accessibility, camera issues).
Step 2: Confirm MFA Setup
After the user scans the QR code and sees a code in their app, they submit it to confirm setup. This verifies the shared secret is working correctly before enabling MFA.
/api/v1/users/me/mfa/confirm🔒 AuthConfirm MFA setup with a TOTP code from the user's app.
{
"code": "482913"
}Recovery codes: These are shown only once. Instruct users to save them in a password manager or write them down. Each code is single-use — when a user loses their authenticator app, they can log in with a recovery code instead.
Step 3: Handling MFA During Login
When a user with MFA enabled logs in, the POST /auth/login endpoint returns mfa_required: true instead of tokens. Your app should detect this and prompt for the TOTP code.
Frontend Flow
1. User submits email + password
2. POST /auth/login → response contains mfa_required: true, mfa_token: "mfa_..."
3. Your app shows a TOTP input screen
4. User enters 6-digit code from authenticator
5. POST /auth/verify-mfa { mfa_token, code } → tokens returned
6. Login completeImplementation Example
/api/v1/auth/login → /api/v1/auth/verify-mfaPublicComplete login flow with MFA branching.
Enforcing MFA Per-Tenant
By default, MFA is optional — users can choose to enable it. Tenant admins can enforce MFA for all users via the settings API. When enforced, users without MFA are prompted to set it up on their next login.
Enable MFA Enforcement
/api/v1/settings🔒 AuthEnable MFA enforcement for all users in the tenant.
{
"mfa": {
"enabled": true,
"enforced": true
}
}What Happens When MFA is Enforced
- •Users who already have MFA enabled: no change — they continue logging in normally with MFA.
- •Users without MFA: on their next successful password login, the response includes
mfa_setup_required: truewith a temporary token. Your app should redirect them to the MFA setup flow. - •New registrations: after registration, the user must set up MFA before they can use the access token for protected actions.
Grace period: There is no grace period. Once enforcement is enabled, all users without MFA will be required to set it up on their next authentication attempt. Plan your rollout communication accordingly.
Disabling MFA (User)
Users can disable their own MFA (unless enforcement is active). They must provide a valid TOTP code to confirm the action.
/api/v1/users/me/mfa/disable🔒 AuthDisable MFA for the current user (requires TOTP confirmation).
{
"code": "739201"
}Admin MFA Reset
When a user loses access to their authenticator app and doesn't have recovery codes, a tenant admin can reset their MFA. This disables MFA for that user — they'll need to set it up again on next login (if enforcement is active).
/api/v1/users/:id/reset-mfa🔒 AuthReset (disable) MFA for a user — admin action.
This requires users:write permission. See the Users API for full details.
Security consideration: Admin MFA reset should be paired with identity verification (phone call, in-person check, verified support ticket). An attacker who compromises an admin account could use this to bypass MFA for target users. Consider requiring re-authentication for admin reset actions.
Recovery Codes
Recovery codes are generated when MFA is first confirmed. Each code is single-use and can be used in place of a TOTP code during login.
How They Work
- •8 recovery codes are generated at MFA setup time
- •Each code can only be used once — then it's consumed
- •Submit a recovery code to
POST /auth/verify-mfain thecodefield (same endpoint) - •When all codes are used, the user can regenerate them (requires a valid TOTP code)
Regenerate Recovery Codes
/api/v1/users/me/mfa/recovery-codes🔒 AuthRegenerate recovery codes (requires current TOTP code). Old codes are invalidated.
{
"code": "123456"
}Best Practices
Communicate before enforcing
Send users an email or in-app notification 7 days before enabling MFA enforcement. Link to setup instructions. This avoids support tickets from users locked out at enforcement time.
Always show recovery codes prominently
Make users acknowledge they've saved their recovery codes. Consider a checkbox or a "copy to clipboard" button with a confirmation step. Lost recovery codes + lost authenticator = admin reset required.
Handle clock drift gracefully
TOTP codes change every 30 seconds. Vyntech Account accepts ±1 step (the previous and next code), but users with significantly drifted clocks will fail. If repeated failures occur, suggest they sync their device clock.
Don't store MFA tokens in localStorage
The mfa_tokenreturned during login is short-lived (5 min) and should only live in component state. Don't persist it — if the user refreshes, they restart the login flow.
Rate-limit MFA verification in your UI
The backend rate-limits failed MFA attempts (lockout after 5 failures for 15 minutes). Show a clear error message and disable the submit button temporarily on failure to prevent users from triggering the lockout.
What's Next
Authentication Flows →
Visual diagram of the MFA verification flow in context.
Authentication API →
Full reference for /auth/verify-mfa and related endpoints.
Tenant Settings Guide →
Configure MFA enforcement and other security settings.
Risk Engine Guide →
How MFA interacts with the risk engine and adaptive challenges.