Docs/Account/Guides/MFA

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:

  1. 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.
  2. 2Both the server and the app independently derive a 6-digit code from the shared secret + current time (30-second windows).
  3. 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.

POST/api/v1/users/me/mfa/setup🔒 Auth

Initiate MFA setup — returns secret and QR code URI.

curl -X POST https://id.vyntech.com.au/api/v1/users/me/mfa/setup \ -H "Authorization: Bearer eyJhbGciOiJFZERTQSIs..."

Rendering the QR Code

You have two options for displaying the QR code to the user:

  • Use the returned SVG — the qr_code_svg field contains a ready-to-render SVG string. Just inject it into your DOM.
  • Generate client-side — use the provisioning_uri with a library like qrcode.react or qrcode to 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.

POST/api/v1/users/me/mfa/confirm🔒 Auth

Confirm MFA setup with a TOTP code from the user's app.

curl -X POST https://id.vyntech.com.au/api/v1/users/me/mfa/confirm \ -H "Authorization: Bearer eyJhbGciOiJFZERTQSIs..." \ -H "Content-Type: application/json" \ -d '{"code": "482913"}'
Request Body
{
  "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

Login Flow with MFA
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 complete

Implementation Example

POST/api/v1/auth/login → /api/v1/auth/verify-mfaPublic

Complete login flow with MFA branching.

# Step 1: Login curl -X POST https://id.vyntech.com.au/api/v1/auth/login \ -H "Content-Type: application/json" \ -d '{"email": "jane@acme-corp.com", "password": "SecureP@ss2024!", "tenant_id": "tnt_01H7ABCD..."}' # If response contains mfa_required: true, proceed to Step 2: curl -X POST https://id.vyntech.com.au/api/v1/auth/verify-mfa \ -H "Content-Type: application/json" \ -d '{"mfa_token": "mfa_01H8NXYZ...", "code": "482913"}'

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

PATCH/api/v1/settings🔒 Auth

Enable MFA enforcement for all users in the tenant.

curl -X PATCH https://id.vyntech.com.au/api/v1/settings \ -H "Authorization: Bearer eyJhbGciOiJFZERTQSIs..." \ -H "Content-Type: application/json" \ -d '{"mfa": {"enabled": true, "enforced": true}}'
Request Body
{
  "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: true with 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.

POST/api/v1/users/me/mfa/disable🔒 Auth

Disable MFA for the current user (requires TOTP confirmation).

curl -X POST https://id.vyntech.com.au/api/v1/users/me/mfa/disable \ -H "Authorization: Bearer eyJhbGciOiJFZERTQSIs..." \ -H "Content-Type: application/json" \ -d '{"code": "739201"}'
Request Body
{
  "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).

POST/api/v1/users/:id/reset-mfa🔒 Auth

Reset (disable) MFA for a user — admin action.

curl -X POST https://id.vyntech.com.au/api/v1/users/usr_01H8KXYZ4F2B7NQ9RPWT3M6J/reset-mfa \ -H "Authorization: Bearer eyJhbGciOiJFZERTQSIs..."

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-mfa in the code field (same endpoint)
  • When all codes are used, the user can regenerate them (requires a valid TOTP code)

Regenerate Recovery Codes

POST/api/v1/users/me/mfa/recovery-codes🔒 Auth

Regenerate recovery codes (requires current TOTP code). Old codes are invalidated.

curl -X POST https://id.vyntech.com.au/api/v1/users/me/mfa/recovery-codes \ -H "Authorization: Bearer eyJhbGciOiJFZERTQSIs..." \ -H "Content-Type: application/json" \ -d '{"code": "123456"}'
Request Body
{
  "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

We use cookies and similar technologies to measure traffic and improve the site. You can choose which categories to allow. Manage Preferences.