← Back to @payez/next-mvp
2FA

2FA Enforcement

Per-route two-factor authentication. Declarative, not imperative.

Require 2FA on sensitive routes via the page_permissions table. No code changes — just declare which routes need step-up authentication. The middleware handles the challenge flow.

How it works

1

The page_permissions table has a requires_2fa boolean column. Set it to true for any route pattern, and the middleware will enforce 2FA before allowing access.

2

When a user hits a 2FA-required route and hasn't completed 2FA in this session, they're redirected to the 2FA challenge screen. After completing the challenge, they're redirected back to the original route.

3

Supported 2FA methods: SMS code, email code, and TOTP (authenticator app). The IDP manages method enrollment and code generation. The MVP handles the challenge UI and verification.

4

2FA status is stored in the session. Once a user completes 2FA, they don't get challenged again until the session expires or a configurable re-challenge interval passes.

Declaring 2FA requirements

This is the key insight: 2FA enforcement is data, not code. You don't add 2FA checks to your route handlers. You declare which routes need 2FA in the database, and the middleware enforces it. This means: an admin can add 2FA requirements to new routes from the admin dashboard — without a code deployment.

-- Require 2FA for admin and billing routes
INSERT INTO vibe_app.page_permissions
  (route_pattern, required_roles, requires_2fa)
VALUES
  ('/admin/*', '{"vibe_app_admin"}', true),
  ('/billing/*', '{"billing_manager"}', true),
  ('/settings/security/*', NULL, true);

-- route_pattern supports wildcards
-- required_roles: NULL = any authenticated user
-- requires_2fa: true = must complete 2FA challenge

The challenge flow

When middleware detects a 2FA-required route and the session hasn't completed 2FA: 1. The original URL is saved in the session 2. User is redirected to /auth/2fa-challenge 3. The challenge screen shows the user's enrolled methods (SMS, email, TOTP) 4. User selects a method and enters the code 5. The IDP verifies the code 6. On success: session is marked as 2FA-verified, user is redirected to original URL 7. On failure: user can retry or choose a different method The challenge screen is pre-built. It shows masked contact info ("Send code to j***@e***.com") so the user knows where the code is going without exposing full contact details.

Supported methods

Three 2FA methods, all managed by the IDP: - SMS — code sent to the user's verified phone number. Masked display: "***-***-4567" - Email — code sent to the user's email. Masked display: "j***@e***.com" - TOTP — authenticator app (Google Authenticator, Authy, 1Password). QR code enrollment. Users can enroll in multiple methods and choose which one to use at challenge time. If their primary method fails (SMS not delivered), they can fall back to another enrolled method.

2FA session management

Once 2FA is completed, the session is marked as 2FA-verified with a timestamp. Subsequent requests to 2FA-required routes proceed without re-challenge — until the verification expires. Configurable policies: - 2FA verification TTL: how long the 2FA status lasts (default: session lifetime) - Re-challenge interval: force re-verification every N hours even within an active session - Sensitive action re-challenge: require fresh 2FA for specific actions (delete account, change email) even if already verified

Enrollment from the user profile

Users enroll in 2FA from their profile settings. The MVP includes a pre-built 2FA enrollment flow: - Phone verification: enter number → receive SMS code → verify - Email: already verified from account creation - TOTP: scan QR code with authenticator app → enter initial code → verified Admins can see which users have 2FA enrolled and which methods they've set up — from the admin dashboard's Users tab.

This is included. Not extra.

2FA Enforcement ships with every @payez/next-mvp install. 10K MAU free. No credit card required.