A zero-dependency, RFC-compliant TOTP (Time-based One-Time Password) library for Node.js. It is intended for server-side 2FA flows with Google Authenticator, Authy, and other TOTP-compatible apps.
npm install @sourceregistry/node-totp
import { generateURI, verifyToken } from '@sourceregistry/node-totp';
// Generate setup URI for authenticator apps
const { uri, secret } = generateURI({
issuer: 'MyApp',
account: 'user@example.com',
algorithm: 'SHA256',
digits: 6,
period: 30
});
console.log('Scan this URI in your authenticator app:', uri);
// otpauth://totp/MyApp:user%40example.com?issuer=MyApp&secret=...
// Later, verify user input
const userInput = '123456';
const isValid = verifyToken(userInput, secret, {
algorithm: 'SHA256',
digits: 6,
period: 30,
window: 1 // Accept tokens from +/-30 seconds
});
console.log('Token valid:', isValid);
import totp from '@sourceregistry/node-totp';
// Generate with custom secret length (algorithm-appropriate defaults)
const { secret } = totp.generateURI({
issuer: 'SecureApp',
account: 'admin@secureapp.com',
algorithm: 'SHA512', // Uses 64-byte secret by default
byteLength: 48 // Override default secret length
});
// Verify with custom time (useful for testing)
const testTime = Math.floor(Date.now() / 1000);
const testToken = totp.generateToken(
totp.base32.decode(secret),
Math.floor(testTime / 30),
6,
'SHA512'
);
const isValid = totp.verifyToken(testToken, secret, {
algorithm: 'SHA512',
digits: 6,
period: 30,
window: 2,
now: testTime // Use specific timestamp instead of Date.now()
});
generateURI(options)Generates an otpauth:// URI and a secret for TOTP setup.
issuer (string, required): Service name such as MyAppaccount (string, required): User identifier such as an email addresssecret (string, optional): Canonical unpadded Base32 secretalgorithm (string, optional): SHA1, SHA256, or SHA512 (default SHA1)digits (number, optional): 6, 7, or 8 (default 6)period (number, optional): Time step in seconds (default 30)byteLength (number, optional): Secret length in bytesReturns { uri: string, secret: string }.
verifyToken(token, secret, options?)Verifies a TOTP token against a secret.
token (string): User-provided token with 6 to 8 digitssecret (string): Canonical unpadded Base32 secretoptions.window (number): Time window in steps (default 1, meaning +/-30 seconds with the default period)options.period (number): Time step in seconds (default 30)options.algorithm (string): Hash algorithm (default SHA1)options.digits (number): Expected token length (default 6)options.now (number): Unix timestamp in seconds, useful for testingReturns boolean.
generateToken(secret, counter, digits, algorithm)Generates a TOTP token.
secret (Buffer): Decoded secret buffercounter (number): Time step counterdigits (number): Token lengthalgorithm (string): Hash algorithmReturns a zero-padded string token.
base32RFC 4648 Base32 helpers:
base32.encode(buffer: Buffer): stringbase32.decode(base32Str: string): BufferThe library accepts canonical unpadded Base32 secrets. Padded or non-canonical forms are normalized or rejected before use.
crypto.timingSafeEqual()SHA1, SHA256, and SHA5126, 7, or 8npm test
npm run test:coverage
qrcode package)import qrcode from 'qrcode';
import { generateURI } from '@sourceregistry/node-totp';
const { uri } = generateURI({
issuer: 'MyApp',
account: 'user@example.com'
});
const qrDataUrl = await qrcode.toDataURL(uri);
// Display qrDataUrl in your HTML template
import express from 'express';
import { generateURI, verifyToken } from '@sourceregistry/node-totp';
const app = express();
// Setup route
app.get('/2fa/setup', (req, res) => {
const { uri, secret } = generateURI({
issuer: 'MyApp',
account: req.user.email
});
// Store secret securely (encrypted) in user database
req.user.totpSecret = secret;
res.json({ uri });
});
// Verify route
app.post('/2fa/verify', (req, res) => {
const { token } = req.body;
const isValid = verifyToken(token, req.user.totpSecret, {
window: 1
});
res.json({ valid: isValid });
});
Apache-2.0 © A.P.A. Slaa
Note: This library is designed for server-side Node.js applications. For browser usage, consider using a Web Crypto API compatible alternative.