Script Valley
Authentication From Scratch
Password Reset and MFALesson 6.3

How to set up TOTP in Node.js with speakeasy

speakeasy package, secret generation, QR code generation, TOTP verification, window parameter, enabling MFA on user record, step-by-step setup flow

Generating and Verifying TOTP

npm install speakeasy qrcode
const speakeasy = require('speakeasy');
const QRCode = require('qrcode');

// Step 1: Generate a secret (called when user starts MFA setup)
const secret = speakeasy.generateSecret({
  name: `MyApp (${user.email})`
});

// secret.base32 โ€” store this temporarily until user confirms
// secret.otpauth_url โ€” encode as QR code

const qrCodeUrl = await QRCode.toDataURL(secret.otpauth_url);
// Send qrCodeUrl to front end as an 
// Step 2: Verify the code the user types to confirm setup
const verified = speakeasy.totp.verify({
  secret: secret.base32,
  encoding: 'base32',
  token: req.body.code,
  window: 1 // allow 1 step of clock drift
});

if (verified) {
  await db.setUserTotpSecret(user.id, secret.base32);
  await db.setMfaEnabled(user.id, true);
  res.json({ success: true, backupCodes: generateBackupCodes() });
} else {
  res.status(400).json({ error: 'Invalid code, setup failed' });
}

The window: 1 parameter allows codes from the previous and next 30-second window, accommodating small clock drift between the user's device and your server. Do not set it higher than 1โ€“2 without good reason.

Up next

How to enforce MFA during the login flow

Sign in to track progress

How to set up TOTP in Node.js with speakeasy โ€” Password Reset and MFA โ€” Authentication From Scratch โ€” Script Valley โ€” Script Valley