Script Valley
Authentication From Scratch
Password Reset and MFALesson 6.4

How to enforce MFA during the login flow

two-step login, partial authentication state, MFA challenge step, session state machine, MFA bypass risks, remember device token

The Two-Step Login State Machine

MFA requires a two-phase login. After verifying the password, do not immediately authenticate the user. Set a partial state in the session and prompt for the TOTP code.

// Phase 1: password check
app.post('/auth/login', async (req, res) => {
  const user = await verifyCredentials(req.body);
  if (!user) return res.status(401).json({ error: 'Invalid credentials' });

  if (user.mfaEnabled) {
    req.session.pendingUserId = user.id;
    return res.json({ mfaRequired: true });
  }

  req.session.regenerate((err) => {
    req.session.userId = user.id;
    res.json({ message: 'Logged in' });
  });
});

// Phase 2: TOTP verification
app.post('/auth/mfa', async (req, res) => {
  const userId = req.session.pendingUserId;
  if (!userId) return res.status(401).json({ error: 'No pending login' });

  const user = await db.findById(userId);
  const valid = speakeasy.totp.verify({
    secret: user.totpSecret,
    encoding: 'base32',
    token: req.body.code,
    window: 1
  });

  if (!valid) return res.status(401).json({ error: 'Invalid code' });

  delete req.session.pendingUserId;
  req.session.regenerate((err) => {
    req.session.userId = user.id;
    res.json({ message: 'Logged in' });
  });
});

Up next

How to use backup codes for MFA account recovery

Sign in to track progress