Multi-Factor Authentication (TOTP)

streetjs ships standards-based TOTP MFA (RFC 6238, built on RFC 4226 HOTP) with single-use recovery codes, an enrollment service, a step-up guard, and middleware. Pure node:crypto — no third-party dependencies. The TOTP/HOTP implementation is validated against the published RFC test vectors (tests/mfa.test.ts).

Primitives

1
2
3
4
import { totp, verifyTotp, base32Encode, base32Decode } from 'streetjs';
const secret = base32Decode('JBSWY3DPEHPK3PXP');
const code = totp(secret);                 // 6-digit, 30s period, SHA1 (app default)
verifyTotp(secret, code, { window: 1 });   // ±1 period skew, constant-time compare

Enrollment & verification (MfaService)

1
2
3
4
5
6
7
8
9
10
import { MfaService, MFA_MIGRATION_SQL } from 'streetjs';
// run MFA_MIGRATION_SQL once (creates street_mfa)
const mfa = new MfaService(pool, { issuer: 'Acme' });

const { secret, otpauthUrl, recoveryCodes } = await mfa.beginEnrollment(userId, 'alice@acme.com');
// show otpauthUrl as a QR code; show recoveryCodes once
await mfa.confirmEnrollment(userId, userSuppliedCode); // enables MFA on success
await mfa.verify(userId, code);                        // ongoing verification
await mfa.useRecoveryCode(userId, recoveryCode);       // single-use
await mfa.disable(userId);

Secrets are stored as base32; recovery codes are stored only as SHA-256 hashes and removed on use.

Step-up middleware (mfaGuard + verifyMfaStepUp)

1
2
3
4
5
6
7
8
import { mfaGuard, verifyMfaStepUp } from 'streetjs';

// Protect routes: MFA-enabled users must have an MFA-verified session.
app.use(mfaGuard(mfa)); // 403 { error: 'mfa_required' } until verified

// In your MFA-challenge route, after the user submits a code:
const result = await verifyMfaStepUp(mfa, userId, code, ctx); // marks ctx.state.mfaVerified
if (result.ok) { /* issue MFA-elevated session */ }

Users without MFA enabled and unauthenticated requests pass through unchanged (pair with an auth guard upstream).

Verification

1
2
npx tsc -p packages/core
node --test packages/core/dist/src/tests/mfa.test.js   # 18 tests, 0 fail

Covered: RFC 4226 HOTP (10 counters), RFC 6238 TOTP (6 time vectors), base32 round-trip + RFC 4648 vector, skew tolerance, enrollment lifecycle, single-use recovery codes, and the step-up guard/verify paths.

Limitations

  • TOTP and WebAuthn (passkeys, shipped separately) are the supported factors; SMS/email OTP are intentionally not provided (phishing/SIM-swap risk).