DEV Community

Amanullah
Amanullah

Posted on

Add Google Authenticator 2FA to your Node app in two steps

Adding 2FA to an app sounds like a weekend of reading RFCs, so most side projects never get it. I published 2fa-kit to make it two steps.

Step 1, enrol: generate a secret, show it as a QR code, store it encrypted.

import { createVault, generateSecret, buildUri } from "2fa-kit";

const vault = await createVault(process.env.MASTER_KEY!);
const secret = await generateSecret();
const uri = buildUri({ label: user.email, secret, issuer: "Acme" });
// render `uri` as a QR code, or show `secret` for manual entry

const { encrypted, salt } = await vault.encrypt(secret);
// save both on the user record
Enter fullscreen mode Exit fullscreen mode

Step 2, verify at login:

import { verifyTotpWithDelta } from "2fa-kit";

const secret = await vault.decrypt(user.totpSecret, user.totpSalt);
const { valid, step } = await verifyTotpWithDelta(secret, codeFromForm);

if (!valid || step! <= user.totpLastStep) deny();
user.totpLastStep = step!; // persist, then allow
Enter fullscreen mode Exit fullscreen mode

That is the whole integration. It works with Google Authenticator, Authy, 1Password, or any TOTP app, and it handles a few things that are easy to get wrong by hand:

  • Secrets are encrypted at rest, not sitting plaintext in your database
  • Replayed codes are rejected (that step check, required by RFC 6238 and skipped by most tutorials)
  • Backup codes for when the user loses their phone, stored as keyed hashes
  • Importing existing Google Authenticator accounts via their migration QR

Zero dependencies. Runs on Node 20+, Bun, Deno, and browsers.

Docs and source: github.com/amansoomro062/2fa-kit.

What has stopped you from adding 2FA to your own projects?

Top comments (0)